import { Response, NextFunction } from 'express';
import { z } from 'zod';
import { invoicesService, CreateInvoiceDto, UpdateInvoiceDto, CreatePaymentDto } from './services/index.js';
import { ApiResponse, AuthenticatedRequest, ValidationError } from '../../shared/types/index.js';

// Validation schemas
const createInvoiceSchema = z.object({
  invoiceType: z.enum(['sale', 'purchase', 'credit_note', 'debit_note']),
  partnerId: z.string().uuid('Partner ID inválido'),
  invoiceDate: z.string().optional(),
  dueDate: z.string().optional(),
  currency: z.string().length(3).default('MXN'),
  subtotal: z.coerce.number().optional(),
  taxAmount: z.coerce.number().optional(),
  totalAmount: z.coerce.number().optional(),
  notes: z.string().optional(),
});

const updateInvoiceSchema = z.object({
  partnerId: z.string().uuid().optional(),
  invoiceDate: z.string().optional(),
  dueDate: z.string().optional(),
  currency: z.string().length(3).optional(),
  subtotal: z.coerce.number().optional(),
  taxAmount: z.coerce.number().optional(),
  totalAmount: z.coerce.number().optional(),
  notes: z.string().optional().nullable(),
  status: z.enum(['draft', 'validated', 'paid', 'cancelled']).optional(),
});

const querySchema = z.object({
  invoiceType: z.enum(['sale', 'purchase', 'credit_note', 'debit_note']).optional(),
  partnerId: z.string().uuid().optional(),
  status: z.enum(['draft', 'validated', 'paid', 'cancelled']).optional(),
  limit: z.coerce.number().int().positive().max(100).default(50),
  offset: z.coerce.number().int().min(0).default(0),
});

const createPaymentSchema = z.object({
  partnerId: z.string().uuid('Partner ID inválido'),
  paymentType: z.enum(['received', 'made']),
  paymentMethod: z.string().min(1, 'Método de pago requerido'),
  amount: z.coerce.number().positive('Monto debe ser positivo'),
  currency: z.string().length(3).default('MXN'),
  paymentDate: z.string().optional(),
  reference: z.string().optional(),
  notes: z.string().optional(),
  partnerName: z.string().optional(),
});

const paymentQuerySchema = z.object({
  partnerId: z.string().uuid().optional(),
  limit: z.coerce.number().int().positive().max(100).default(50),
  offset: z.coerce.number().int().min(0).default(0),
});

class InvoicesController {
  // ========== INVOICES ==========

  async findAll(req: AuthenticatedRequest, res: Response, next: NextFunction) {
    try {
      const tenantId = req.user?.tenantId;
      if (!tenantId) {
        return res.status(400).json({ success: false, error: 'Tenant ID required' } as ApiResponse);
      }

      const validation = querySchema.safeParse(req.query);
      if (!validation.success) {
        throw new ValidationError('Parámetros de consulta inválidos', validation.error.errors);
      }

      const result = await invoicesService.findAllInvoices({
        tenantId,
        ...validation.data,
      });

      res.json({ success: true, ...result } as ApiResponse);
    } catch (error) {
      next(error);
    }
  }

  async findById(req: AuthenticatedRequest, res: Response, next: NextFunction) {
    try {
      const tenantId = req.user?.tenantId;
      if (!tenantId) {
        return res.status(400).json({ success: false, error: 'Tenant ID required' } as ApiResponse);
      }

      const invoice = await invoicesService.findInvoice(req.params.id, tenantId);
      if (!invoice) {
        return res.status(404).json({ success: false, error: 'Factura no encontrada' } as ApiResponse);
      }

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

  async create(req: AuthenticatedRequest, res: Response, next: NextFunction) {
    try {
      const tenantId = req.user?.tenantId;
      const userId = req.user?.userId;
      if (!tenantId) {
        return res.status(400).json({ success: false, error: 'Tenant ID required' } as ApiResponse);
      }

      const validation = createInvoiceSchema.safeParse(req.body);
      if (!validation.success) {
        throw new ValidationError('Datos de factura inválidos', validation.error.errors);
      }

      const invoice = await invoicesService.createInvoice(tenantId, validation.data as CreateInvoiceDto, userId);
      res.status(201).json({ success: true, data: invoice } as ApiResponse);
    } catch (error) {
      next(error);
    }
  }

  async update(req: AuthenticatedRequest, res: Response, next: NextFunction) {
    try {
      const tenantId = req.user?.tenantId;
      const userId = req.user?.userId;
      if (!tenantId) {
        return res.status(400).json({ success: false, error: 'Tenant ID required' } as ApiResponse);
      }

      const validation = updateInvoiceSchema.safeParse(req.body);
      if (!validation.success) {
        throw new ValidationError('Datos de actualización inválidos', validation.error.errors);
      }

      const invoice = await invoicesService.updateInvoice(req.params.id, tenantId, validation.data as UpdateInvoiceDto, userId);
      if (!invoice) {
        return res.status(404).json({ success: false, error: 'Factura no encontrada' } as ApiResponse);
      }

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

  async delete(req: AuthenticatedRequest, res: Response, next: NextFunction) {
    try {
      const tenantId = req.user?.tenantId;
      if (!tenantId) {
        return res.status(400).json({ success: false, error: 'Tenant ID required' } as ApiResponse);
      }

      const deleted = await invoicesService.deleteInvoice(req.params.id, tenantId);
      if (!deleted) {
        return res.status(404).json({ success: false, error: 'Factura no encontrada' } as ApiResponse);
      }

      res.status(204).send();
    } catch (error) {
      next(error);
    }
  }

  async validate(req: AuthenticatedRequest, res: Response, next: NextFunction) {
    try {
      const tenantId = req.user?.tenantId;
      const userId = req.user?.userId;
      if (!tenantId) {
        return res.status(400).json({ success: false, error: 'Tenant ID required' } as ApiResponse);
      }

      const invoice = await invoicesService.validateInvoice(req.params.id, tenantId, userId);
      if (!invoice) {
        return res.status(400).json({ success: false, error: 'No se puede validar la factura' } as ApiResponse);
      }

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

  async cancel(req: AuthenticatedRequest, res: Response, next: NextFunction) {
    try {
      const tenantId = req.user?.tenantId;
      const userId = req.user?.userId;
      if (!tenantId) {
        return res.status(400).json({ success: false, error: 'Tenant ID required' } as ApiResponse);
      }

      const invoice = await invoicesService.cancelInvoice(req.params.id, tenantId, userId);
      if (!invoice) {
        return res.status(400).json({ success: false, error: 'No se puede cancelar la factura' } as ApiResponse);
      }

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

  // ========== PAYMENTS ==========

  async findAllPayments(req: AuthenticatedRequest, res: Response, next: NextFunction) {
    try {
      const tenantId = req.user?.tenantId;
      if (!tenantId) {
        return res.status(400).json({ success: false, error: 'Tenant ID required' } as ApiResponse);
      }

      const validation = paymentQuerySchema.safeParse(req.query);
      if (!validation.success) {
        throw new ValidationError('Parámetros de consulta inválidos', validation.error.errors);
      }

      const { partnerId, limit, offset } = validation.data;
      const result = await invoicesService.findAllPayments(tenantId, partnerId, limit, offset);

      res.json({ success: true, ...result } as ApiResponse);
    } catch (error) {
      next(error);
    }
  }

  async findPaymentById(req: AuthenticatedRequest, res: Response, next: NextFunction) {
    try {
      const tenantId = req.user?.tenantId;
      if (!tenantId) {
        return res.status(400).json({ success: false, error: 'Tenant ID required' } as ApiResponse);
      }

      const payment = await invoicesService.findPayment(req.params.id, tenantId);
      if (!payment) {
        return res.status(404).json({ success: false, error: 'Pago no encontrado' } as ApiResponse);
      }

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

  async createPayment(req: AuthenticatedRequest, res: Response, next: NextFunction) {
    try {
      const tenantId = req.user?.tenantId;
      const userId = req.user?.userId;
      if (!tenantId) {
        return res.status(400).json({ success: false, error: 'Tenant ID required' } as ApiResponse);
      }

      const validation = createPaymentSchema.safeParse(req.body);
      if (!validation.success) {
        throw new ValidationError('Datos de pago inválidos', validation.error.errors);
      }

      const payment = await invoicesService.createPayment(tenantId, validation.data as CreatePaymentDto, userId);
      res.status(201).json({ success: true, data: payment } as ApiResponse);
    } catch (error) {
      next(error);
    }
  }

  async confirmPayment(req: AuthenticatedRequest, res: Response, next: NextFunction) {
    try {
      const tenantId = req.user?.tenantId;
      const userId = req.user?.userId;
      if (!tenantId) {
        return res.status(400).json({ success: false, error: 'Tenant ID required' } as ApiResponse);
      }

      const payment = await invoicesService.confirmPayment(req.params.id, tenantId, userId);
      if (!payment) {
        return res.status(400).json({ success: false, error: 'No se puede confirmar el pago' } as ApiResponse);
      }

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

  async cancelPayment(req: AuthenticatedRequest, res: Response, next: NextFunction) {
    try {
      const tenantId = req.user?.tenantId;
      const userId = req.user?.userId;
      if (!tenantId) {
        return res.status(400).json({ success: false, error: 'Tenant ID required' } as ApiResponse);
      }

      const payment = await invoicesService.cancelPayment(req.params.id, tenantId, userId);
      if (!payment) {
        return res.status(400).json({ success: false, error: 'No se puede cancelar el pago' } as ApiResponse);
      }

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

export const invoicesController = new InvoicesController();
