/**
 * Partner Contact Controller
 * API endpoints para gestion de contactos de partners
 *
 * @module Partners
 * @prefix /api/v1/partners/:partnerId/contacts
 */

import { Router, Request, Response, NextFunction } from 'express';
import { DataSource } from 'typeorm';
import {
  PartnerContactService,
  CreatePartnerContactDto,
  UpdatePartnerContactDto,
  PartnerContactFilters,
  ServiceContext,
} from '../services/partner-contact.service';
import { PartnerService } from '../services/partner.service';

export function createPartnerContactController(dataSource: DataSource): Router {
  const router = Router({ mergeParams: true });
  const contactService = new PartnerContactService(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/contacts
   * Lista todos los contactos 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 { isPrimary, isBillingContact, isShippingContact, receivesNotifications, search } = req.query;

      const filters: PartnerContactFilters = {};
      if (isPrimary !== undefined) filters.isPrimary = isPrimary === 'true';
      if (isBillingContact !== undefined) filters.isBillingContact = isBillingContact === 'true';
      if (isShippingContact !== undefined) filters.isShippingContact = isShippingContact === 'true';
      if (receivesNotifications !== undefined) filters.receivesNotifications = receivesNotifications === 'true';
      if (search) filters.search = search as string;

      const contacts = await contactService.findByPartnerId(req.params.partnerId, filters);
      return res.json({ success: true, data: contacts, count: contacts.length });
    } catch (error) {
      return next(error);
    }
  });

  /**
   * GET /api/v1/partners/:partnerId/contacts/primary
   * Obtiene el contacto principal
   */
  router.get('/primary', 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 contact = await contactService.findPrimaryContact(req.params.partnerId);
      if (!contact) {
        return res.status(404).json({ success: false, error: 'No hay contacto principal' });
      }

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

  /**
   * GET /api/v1/partners/:partnerId/contacts/billing
   * Lista contactos de facturacion
   */
  router.get('/billing', 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 contacts = await contactService.findBillingContacts(req.params.partnerId);
      return res.json({ success: true, data: contacts, count: contacts.length });
    } catch (error) {
      return next(error);
    }
  });

  /**
   * GET /api/v1/partners/:partnerId/contacts/shipping
   * Lista contactos de envio
   */
  router.get('/shipping', 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 contacts = await contactService.findShippingContacts(req.params.partnerId);
      return res.json({ success: true, data: contacts, count: contacts.length });
    } catch (error) {
      return next(error);
    }
  });

  /**
   * GET /api/v1/partners/:partnerId/contacts/notifications
   * Lista contactos que reciben notificaciones
   */
  router.get('/notifications', 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 contacts = await contactService.findNotificationRecipients(req.params.partnerId);
      return res.json({ success: true, data: contacts, count: contacts.length });
    } catch (error) {
      return next(error);
    }
  });

  /**
   * GET /api/v1/partners/:partnerId/contacts/summary
   * Obtiene resumen de contactos
   */
  router.get('/summary', 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 summary = await contactService.getContactSummary(req.params.partnerId);
      return res.json({ success: true, data: summary });
    } catch (error) {
      return next(error);
    }
  });

  /**
   * GET /api/v1/partners/:partnerId/contacts/:id
   * Obtiene un contacto por ID
   */
  router.get('/:id', 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 contact = await contactService.findById(req.params.id);
      if (!contact || contact.partnerId !== req.params.partnerId) {
        return res.status(404).json({ success: false, error: 'Contacto no encontrado' });
      }

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

  /**
   * POST /api/v1/partners/:partnerId/contacts
   * Crea un nuevo contacto
   */
  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: CreatePartnerContactDto = {
        ...req.body,
        partnerId: req.params.partnerId,
      };

      // Validate required fields
      if (!dto.fullName) {
        return res.status(400).json({
          success: false,
          error: 'fullName es requerido',
        });
      }

      const contact = await contactService.create(ctx, dto);
      return res.status(201).json({ success: true, data: contact });
    } catch (error) {
      return next(error);
    }
  });

  /**
   * PATCH /api/v1/partners/:partnerId/contacts/:id
   * Actualiza un contacto
   */
  router.patch('/:id', async (req: Request, res: Response, next: NextFunction) => {
    try {
      if (!validateTenant(req, res)) return;
      const ctx = getContext(req);
      if (!(await validatePartner(req, res, ctx))) return;

      // Verify contact belongs to partner
      const existing = await contactService.findById(req.params.id);
      if (!existing || existing.partnerId !== req.params.partnerId) {
        return res.status(404).json({ success: false, error: 'Contacto no encontrado' });
      }

      const dto: UpdatePartnerContactDto = req.body;
      const contact = await contactService.update(ctx, req.params.id, dto);

      if (!contact) {
        return res.status(404).json({ success: false, error: 'Contacto no encontrado' });
      }

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

  /**
   * PATCH /api/v1/partners/:partnerId/contacts/:id/primary
   * Establece un contacto como principal
   */
  router.patch('/:id/primary', async (req: Request, res: Response, next: NextFunction) => {
    try {
      if (!validateTenant(req, res)) return;
      const ctx = getContext(req);
      if (!(await validatePartner(req, res, ctx))) return;

      // Verify contact belongs to partner
      const existing = await contactService.findById(req.params.id);
      if (!existing || existing.partnerId !== req.params.partnerId) {
        return res.status(404).json({ success: false, error: 'Contacto no encontrado' });
      }

      const contact = await contactService.setAsPrimary(req.params.id);
      if (!contact) {
        return res.status(404).json({ success: false, error: 'Contacto no encontrado' });
      }

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

  /**
   * DELETE /api/v1/partners/:partnerId/contacts/:id
   * Elimina un contacto
   */
  router.delete('/:id', async (req: Request, res: Response, next: NextFunction) => {
    try {
      if (!validateTenant(req, res)) return;
      const ctx = getContext(req);
      if (!(await validatePartner(req, res, ctx))) return;

      // Verify contact belongs to partner
      const existing = await contactService.findById(req.params.id);
      if (!existing || existing.partnerId !== req.params.partnerId) {
        return res.status(404).json({ success: false, error: 'Contacto no encontrado' });
      }

      const deleted = await contactService.delete(req.params.id);
      if (!deleted) {
        return res.status(404).json({ success: false, error: 'Contacto no encontrado' });
      }

      return res.json({ success: true, message: 'Contacto eliminado' });
    } catch (error) {
      return next(error);
    }
  });

  return router;
}

export default createPartnerContactController;
