import { Response, NextFunction } from 'express';
import { z } from 'zod';
import { AuthenticatedRequest } from '../../shared/types/index.js';
import { reportsService } from './reports.service.js';

// ============================================================================
// VALIDATION SCHEMAS
// ============================================================================

const reportFiltersSchema = z.object({
  report_type: z.enum(['financial', 'accounting', 'tax', 'management', 'custom']).optional(),
  category: z.string().optional(),
  is_system: z.coerce.boolean().optional(),
  search: z.string().optional(),
  page: z.coerce.number().min(1).default(1),
  limit: z.coerce.number().min(1).max(100).default(20),
});

const createDefinitionSchema = z.object({
  code: z.string().min(1).max(50),
  name: z.string().min(1).max(255),
  description: z.string().optional(),
  report_type: z.enum(['financial', 'accounting', 'tax', 'management', 'custom']).optional(),
  category: z.string().optional(),
  base_query: z.string().optional(),
  query_function: z.string().optional(),
  parameters_schema: z.record(z.any()).optional(),
  columns_config: z.array(z.any()).optional(),
  export_formats: z.array(z.string()).optional(),
  required_permissions: z.array(z.string()).optional(),
});

const executeReportSchema = z.object({
  definition_id: z.string().uuid(),
  parameters: z.record(z.any()),
});

const createScheduleSchema = z.object({
  definition_id: z.string().uuid(),
  name: z.string().min(1).max(255),
  cron_expression: z.string().min(1),
  default_parameters: z.record(z.any()).optional(),
  company_id: z.string().uuid().optional(),
  timezone: z.string().optional(),
  delivery_method: z.enum(['none', 'email', 'storage', 'webhook']).optional(),
  delivery_config: z.record(z.any()).optional(),
});

const trialBalanceSchema = z.object({
  company_id: z.string().uuid().optional(),
  date_from: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
  date_to: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
  include_zero: z.coerce.boolean().optional(),
});

const generalLedgerSchema = z.object({
  company_id: z.string().uuid().optional(),
  account_id: z.string().uuid(),
  date_from: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
  date_to: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
});

// ============================================================================
// CONTROLLER
// ============================================================================

class ReportsController {
  // ==================== DEFINITIONS ====================

  /**
   * GET /reports/definitions
   * List all report definitions
   */
  async findAllDefinitions(
    req: AuthenticatedRequest,
    res: Response,
    next: NextFunction
  ): Promise<void> {
    try {
      const filters = reportFiltersSchema.parse(req.query);
      const tenantId = req.user!.tenantId;

      const { data, total } = await reportsService.findAllDefinitions(tenantId, filters);

      res.json({
        success: true,
        data,
        pagination: {
          page: filters.page,
          limit: filters.limit,
          total,
          totalPages: Math.ceil(total / filters.limit),
        },
      });
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /reports/definitions/:id
   * Get a specific report definition
   */
  async findDefinitionById(
    req: AuthenticatedRequest,
    res: Response,
    next: NextFunction
  ): Promise<void> {
    try {
      const { id } = req.params;
      const tenantId = req.user!.tenantId;

      const definition = await reportsService.findDefinitionById(id, tenantId);

      res.json({
        success: true,
        data: definition,
      });
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /reports/definitions
   * Create a custom report definition
   */
  async createDefinition(
    req: AuthenticatedRequest,
    res: Response,
    next: NextFunction
  ): Promise<void> {
    try {
      const dto = createDefinitionSchema.parse(req.body);
      const tenantId = req.user!.tenantId;
      const userId = req.user!.userId;

      const definition = await reportsService.createDefinition(dto, tenantId, userId);

      res.status(201).json({
        success: true,
        message: 'Definición de reporte creada exitosamente',
        data: definition,
      });
    } catch (error) {
      next(error);
    }
  }

  // ==================== EXECUTIONS ====================

  /**
   * POST /reports/execute
   * Execute a report
   */
  async executeReport(
    req: AuthenticatedRequest,
    res: Response,
    next: NextFunction
  ): Promise<void> {
    try {
      const dto = executeReportSchema.parse(req.body);
      const tenantId = req.user!.tenantId;
      const userId = req.user!.userId;

      const execution = await reportsService.executeReport(dto, tenantId, userId);

      res.status(202).json({
        success: true,
        message: 'Reporte en ejecución',
        data: execution,
      });
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /reports/executions/:id
   * Get execution details and results
   */
  async findExecutionById(
    req: AuthenticatedRequest,
    res: Response,
    next: NextFunction
  ): Promise<void> {
    try {
      const { id } = req.params;
      const tenantId = req.user!.tenantId;

      const execution = await reportsService.findExecutionById(id, tenantId);

      res.json({
        success: true,
        data: execution,
      });
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /reports/executions
   * Get recent executions
   */
  async findRecentExecutions(
    req: AuthenticatedRequest,
    res: Response,
    next: NextFunction
  ): Promise<void> {
    try {
      const { definition_id, limit } = req.query;
      const tenantId = req.user!.tenantId;

      const executions = await reportsService.findRecentExecutions(
        tenantId,
        definition_id as string,
        parseInt(limit as string) || 20
      );

      res.json({
        success: true,
        data: executions,
      });
    } catch (error) {
      next(error);
    }
  }

  // ==================== SCHEDULES ====================

  /**
   * GET /reports/schedules
   * List all schedules
   */
  async findAllSchedules(
    req: AuthenticatedRequest,
    res: Response,
    next: NextFunction
  ): Promise<void> {
    try {
      const tenantId = req.user!.tenantId;
      const schedules = await reportsService.findAllSchedules(tenantId);

      res.json({
        success: true,
        data: schedules,
      });
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /reports/schedules
   * Create a schedule
   */
  async createSchedule(
    req: AuthenticatedRequest,
    res: Response,
    next: NextFunction
  ): Promise<void> {
    try {
      const dto = createScheduleSchema.parse(req.body);
      const tenantId = req.user!.tenantId;
      const userId = req.user!.userId;

      const schedule = await reportsService.createSchedule(dto, tenantId, userId);

      res.status(201).json({
        success: true,
        message: 'Programación creada exitosamente',
        data: schedule,
      });
    } catch (error) {
      next(error);
    }
  }

  /**
   * PATCH /reports/schedules/:id/toggle
   * Enable/disable a schedule
   */
  async toggleSchedule(
    req: AuthenticatedRequest,
    res: Response,
    next: NextFunction
  ): Promise<void> {
    try {
      const { id } = req.params;
      const { is_active } = req.body;
      const tenantId = req.user!.tenantId;

      const schedule = await reportsService.toggleSchedule(id, tenantId, is_active);

      res.json({
        success: true,
        message: is_active ? 'Programación activada' : 'Programación desactivada',
        data: schedule,
      });
    } catch (error) {
      next(error);
    }
  }

  /**
   * DELETE /reports/schedules/:id
   * Delete a schedule
   */
  async deleteSchedule(
    req: AuthenticatedRequest,
    res: Response,
    next: NextFunction
  ): Promise<void> {
    try {
      const { id } = req.params;
      const tenantId = req.user!.tenantId;

      await reportsService.deleteSchedule(id, tenantId);

      res.json({
        success: true,
        message: 'Programación eliminada',
      });
    } catch (error) {
      next(error);
    }
  }

  // ==================== QUICK REPORTS ====================

  /**
   * GET /reports/quick/trial-balance
   * Generate trial balance directly
   */
  async getTrialBalance(
    req: AuthenticatedRequest,
    res: Response,
    next: NextFunction
  ): Promise<void> {
    try {
      const params = trialBalanceSchema.parse(req.query);
      const tenantId = req.user!.tenantId;

      const data = await reportsService.generateTrialBalance(
        tenantId,
        params.company_id || null,
        params.date_from,
        params.date_to,
        params.include_zero || false
      );

      // Calculate totals
      const totals = {
        initial_debit: 0,
        initial_credit: 0,
        period_debit: 0,
        period_credit: 0,
        final_debit: 0,
        final_credit: 0,
      };

      for (const row of data) {
        totals.initial_debit += parseFloat(row.initial_debit) || 0;
        totals.initial_credit += parseFloat(row.initial_credit) || 0;
        totals.period_debit += parseFloat(row.period_debit) || 0;
        totals.period_credit += parseFloat(row.period_credit) || 0;
        totals.final_debit += parseFloat(row.final_debit) || 0;
        totals.final_credit += parseFloat(row.final_credit) || 0;
      }

      res.json({
        success: true,
        data,
        summary: {
          row_count: data.length,
          totals,
        },
        parameters: params,
      });
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /reports/quick/general-ledger
   * Generate general ledger directly
   */
  async getGeneralLedger(
    req: AuthenticatedRequest,
    res: Response,
    next: NextFunction
  ): Promise<void> {
    try {
      const params = generalLedgerSchema.parse(req.query);
      const tenantId = req.user!.tenantId;

      const data = await reportsService.generateGeneralLedger(
        tenantId,
        params.company_id || null,
        params.account_id,
        params.date_from,
        params.date_to
      );

      // Calculate totals
      const totals = {
        debit: 0,
        credit: 0,
      };

      for (const row of data) {
        totals.debit += parseFloat(row.debit) || 0;
        totals.credit += parseFloat(row.credit) || 0;
      }

      res.json({
        success: true,
        data,
        summary: {
          row_count: data.length,
          totals,
          final_balance: data.length > 0 ? data[data.length - 1].running_balance : 0,
        },
        parameters: params,
      });
    } catch (error) {
      next(error);
    }
  }
}

export const reportsController = new ReportsController();
