/**
 * Partner Tax Info Controller
 * API endpoints para gestion de informacion fiscal de partners
 *
 * @module Partners
 * @prefix /api/v1/partners/:partnerId/tax-info
 */

import { Router, Request, Response, NextFunction } from 'express';
import { DataSource } from 'typeorm';
import {
  PartnerTaxInfoService,
  CreatePartnerTaxInfoDto,
  UpdatePartnerTaxInfoDto,
  VerifyTaxInfoDto,
  ServiceContext,
} from '../services/partner-tax-info.service';
import { PartnerService } from '../services/partner.service';

export function createPartnerTaxInfoController(dataSource: DataSource): Router {
  const router = Router({ mergeParams: true });
  const taxInfoService = new PartnerTaxInfoService(dataSource);
  const partnerService = new PartnerService(dataSource);

  /**
   * Helper to extract service context from request
   */
  function getContext(req: Request): ServiceContext {
    const tenantId = req.headers['x-tenant-id'] as string;
    const userId = (req as any).user?.id;
    return { tenantId, userId };
  }

  /**
   * Validate tenant header
   */
  function validateTenant(req: Request, res: Response): boolean {
    const tenantId = req.headers['x-tenant-id'];
    if (!tenantId) {
      res.status(400).json({ success: false, error: 'X-Tenant-Id header required' });
      return false;
    }
    return true;
  }

  /**
   * Validate partner exists and belongs to tenant
   */
  async function validatePartner(
    req: Request,
    res: Response,
    ctx: ServiceContext
  ): Promise<boolean> {
    const partner = await partnerService.findById(ctx, req.params.partnerId);
    if (!partner) {
      res.status(404).json({ success: false, error: 'Partner no encontrado' });
      return false;
    }
    return true;
  }

  /**
   * GET /api/v1/partners/:partnerId/tax-info
   * Obtiene la informacion fiscal de un partner
   */
  router.get('/', async (req: Request, res: Response, next: NextFunction) => {
    try {
      if (!validateTenant(req, res)) return;
      const ctx = getContext(req);
      if (!(await validatePartner(req, res, ctx))) return;

      const taxInfo = await taxInfoService.findByPartnerId(req.params.partnerId);
      if (!taxInfo) {
        return res.status(404).json({ success: false, error: 'Informacion fiscal no encontrada' });
      }

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

  /**
   * GET /api/v1/partners/:partnerId/tax-info/withholdings
   * Obtiene totales de retenciones
   */
  router.get('/withholdings', async (req: Request, res: Response, next: NextFunction) => {
    try {
      if (!validateTenant(req, res)) return;
      const ctx = getContext(req);
      if (!(await validatePartner(req, res, ctx))) return;

      const withholdings = await taxInfoService.getWithholdingTotals(req.params.partnerId);
      return res.json({ success: true, data: withholdings });
    } catch (error) {
      return next(error);
    }
  });

  /**
   * GET /api/v1/partners/catalogs/sat-regimes
   * Lista regimenes fiscales SAT
   */
  router.get('/catalogs/sat-regimes', async (_req: Request, res: Response, next: NextFunction) => {
    try {
      const regimes = taxInfoService.getSatRegimes();
      const formatted = Object.entries(regimes).map(([code, name]) => ({
        code,
        name,
      }));
      return res.json({ success: true, data: formatted });
    } catch (error) {
      return next(error);
    }
  });

  /**
   * GET /api/v1/partners/catalogs/cfdi-uses
   * Lista usos de CFDI
   */
  router.get('/catalogs/cfdi-uses', async (_req: Request, res: Response, next: NextFunction) => {
    try {
      const uses = taxInfoService.getCfdiUses();
      const formatted = Object.entries(uses).map(([code, name]) => ({
        code,
        name,
      }));
      return res.json({ success: true, data: formatted });
    } catch (error) {
      return next(error);
    }
  });

  /**
   * POST /api/v1/partners/:partnerId/tax-info
   * Crea informacion fiscal
   */
  router.post('/', async (req: Request, res: Response, next: NextFunction) => {
    try {
      if (!validateTenant(req, res)) return;
      const ctx = getContext(req);
      if (!(await validatePartner(req, res, ctx))) return;

      const dto: CreatePartnerTaxInfoDto = {
        ...req.body,
        partnerId: req.params.partnerId,
      };

      const taxInfo = await taxInfoService.create(ctx, dto);
      return res.status(201).json({ success: true, data: taxInfo });
    } catch (error: any) {
      if (error.message?.includes('already exists') || error.message?.includes('Invalid')) {
        return res.status(400).json({ success: false, error: error.message });
      }
      return next(error);
    }
  });

  /**
   * PUT /api/v1/partners/:partnerId/tax-info
   * Crea o actualiza informacion fiscal (upsert)
   */
  router.put('/', async (req: Request, res: Response, next: NextFunction) => {
    try {
      if (!validateTenant(req, res)) return;
      const ctx = getContext(req);
      if (!(await validatePartner(req, res, ctx))) return;

      const dto: Omit<CreatePartnerTaxInfoDto, 'partnerId'> = req.body;
      const taxInfo = await taxInfoService.upsert(ctx, req.params.partnerId, dto);

      return res.json({ success: true, data: taxInfo });
    } catch (error: any) {
      if (error.message?.includes('Invalid')) {
        return res.status(400).json({ success: false, error: error.message });
      }
      return next(error);
    }
  });

  /**
   * PATCH /api/v1/partners/:partnerId/tax-info
   * Actualiza informacion fiscal existente
   */
  router.patch('/', async (req: Request, res: Response, next: NextFunction) => {
    try {
      if (!validateTenant(req, res)) return;
      const ctx = getContext(req);
      if (!(await validatePartner(req, res, ctx))) return;

      const existing = await taxInfoService.findByPartnerId(req.params.partnerId);
      if (!existing) {
        return res.status(404).json({ success: false, error: 'Informacion fiscal no encontrada' });
      }

      const dto: UpdatePartnerTaxInfoDto = req.body;
      const taxInfo = await taxInfoService.update(ctx, existing.id, dto);

      if (!taxInfo) {
        return res.status(404).json({ success: false, error: 'Informacion fiscal no encontrada' });
      }

      return res.json({ success: true, data: taxInfo });
    } catch (error: any) {
      if (error.message?.includes('Invalid')) {
        return res.status(400).json({ success: false, error: error.message });
      }
      return next(error);
    }
  });

  /**
   * PATCH /api/v1/partners/:partnerId/tax-info/verify
   * Verifica la informacion fiscal
   */
  router.patch('/verify', async (req: Request, res: Response, next: NextFunction) => {
    try {
      if (!validateTenant(req, res)) return;
      const ctx = getContext(req);
      if (!(await validatePartner(req, res, ctx))) return;

      const existing = await taxInfoService.findByPartnerId(req.params.partnerId);
      if (!existing) {
        return res.status(404).json({ success: false, error: 'Informacion fiscal no encontrada' });
      }

      const dto: VerifyTaxInfoDto = {
        verificationSource: req.body.verificationSource || 'MANUAL',
      };

      const taxInfo = await taxInfoService.verify(ctx, existing.id, dto);
      if (!taxInfo) {
        return res.status(404).json({ success: false, error: 'Informacion fiscal no encontrada' });
      }

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

  /**
   * PATCH /api/v1/partners/:partnerId/tax-info/unverify
   * Quita la verificacion de la informacion fiscal
   */
  router.patch('/unverify', async (req: Request, res: Response, next: NextFunction) => {
    try {
      if (!validateTenant(req, res)) return;
      const ctx = getContext(req);
      if (!(await validatePartner(req, res, ctx))) return;

      const existing = await taxInfoService.findByPartnerId(req.params.partnerId);
      if (!existing) {
        return res.status(404).json({ success: false, error: 'Informacion fiscal no encontrada' });
      }

      const taxInfo = await taxInfoService.unverify(ctx, existing.id);
      if (!taxInfo) {
        return res.status(404).json({ success: false, error: 'Informacion fiscal no encontrada' });
      }

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

  /**
   * DELETE /api/v1/partners/:partnerId/tax-info
   * Elimina la informacion fiscal
   */
  router.delete('/', async (req: Request, res: Response, next: NextFunction) => {
    try {
      if (!validateTenant(req, res)) return;
      const ctx = getContext(req);
      if (!(await validatePartner(req, res, ctx))) return;

      const existing = await taxInfoService.findByPartnerId(req.params.partnerId);
      if (!existing) {
        return res.status(404).json({ success: false, error: 'Informacion fiscal no encontrada' });
      }

      const deleted = await taxInfoService.delete(existing.id);
      if (!deleted) {
        return res.status(404).json({ success: false, error: 'Informacion fiscal no encontrada' });
      }

      return res.json({ success: true, message: 'Informacion fiscal eliminada' });
    } catch (error) {
      return next(error);
    }
  });

  return router;
}

export default createPartnerTaxInfoController;
