/**
 * Partner Address Controller
 * API endpoints para gestion de direcciones de partners
 *
 * @module Partners
 * @prefix /api/v1/partners/:partnerId/addresses
 */

import { Router, Request, Response, NextFunction } from 'express';
import { DataSource } from 'typeorm';
import {
  PartnerAddressService,
  CreatePartnerAddressDto,
  UpdatePartnerAddressDto,
  ServiceContext,
} from '../services/partner-address.service';
import { PartnerService } from '../services/partner.service';

export function createPartnerAddressController(dataSource: DataSource): Router {
  const router = Router({ mergeParams: true });
  const addressService = new PartnerAddressService(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/addresses
   * Lista todas las direcciones 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 addresses = await addressService.findByPartnerId(req.params.partnerId);
      return res.json({ success: true, data: addresses, count: addresses.length });
    } catch (error) {
      return next(error);
    }
  });

  /**
   * GET /api/v1/partners/:partnerId/addresses/billing
   * Lista direcciones 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 addresses = await addressService.findBillingAddresses(req.params.partnerId);
      return res.json({ success: true, data: addresses, count: addresses.length });
    } catch (error) {
      return next(error);
    }
  });

  /**
   * GET /api/v1/partners/:partnerId/addresses/shipping
   * Lista direcciones 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 addresses = await addressService.findShippingAddresses(req.params.partnerId);
      return res.json({ success: true, data: addresses, count: addresses.length });
    } catch (error) {
      return next(error);
    }
  });

  /**
   * GET /api/v1/partners/:partnerId/addresses/:id
   * Obtiene una direccion 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 address = await addressService.findById(req.params.id);
      if (!address || address.partnerId !== req.params.partnerId) {
        return res.status(404).json({ success: false, error: 'Direccion no encontrada' });
      }

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

  /**
   * GET /api/v1/partners/:partnerId/addresses/:id/formatted
   * Obtiene la direccion formateada como string
   */
  router.get('/:id/formatted', 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 address = await addressService.findById(req.params.id);
      if (!address || address.partnerId !== req.params.partnerId) {
        return res.status(404).json({ success: false, error: 'Direccion no encontrada' });
      }

      const formatted = await addressService.getFormattedAddress(req.params.id);
      return res.json({ success: true, data: { formatted } });
    } catch (error) {
      return next(error);
    }
  });

  /**
   * POST /api/v1/partners/:partnerId/addresses
   * Crea una nueva direccion
   */
  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: CreatePartnerAddressDto = {
        ...req.body,
        partnerId: req.params.partnerId,
      };

      // Validate required fields
      if (!dto.street || !dto.city || !dto.state || !dto.postalCode) {
        return res.status(400).json({
          success: false,
          error: 'street, city, state y postalCode son requeridos',
        });
      }

      // Validate addressType
      if (dto.addressType && !['billing', 'shipping', 'both'].includes(dto.addressType)) {
        return res.status(400).json({
          success: false,
          error: 'addressType debe ser billing, shipping o both',
        });
      }

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

  /**
   * PATCH /api/v1/partners/:partnerId/addresses/:id
   * Actualiza una direccion
   */
  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 address belongs to partner
      const existing = await addressService.findById(req.params.id);
      if (!existing || existing.partnerId !== req.params.partnerId) {
        return res.status(404).json({ success: false, error: 'Direccion no encontrada' });
      }

      const dto: UpdatePartnerAddressDto = req.body;
      const address = await addressService.update(ctx, req.params.id, dto);

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

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

  /**
   * PATCH /api/v1/partners/:partnerId/addresses/:id/default
   * Establece una direccion como predeterminada
   */
  router.patch('/:id/default', 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 address belongs to partner
      const existing = await addressService.findById(req.params.id);
      if (!existing || existing.partnerId !== req.params.partnerId) {
        return res.status(404).json({ success: false, error: 'Direccion no encontrada' });
      }

      const address = await addressService.setAsDefault(req.params.id);
      if (!address) {
        return res.status(404).json({ success: false, error: 'Direccion no encontrada' });
      }

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

  /**
   * DELETE /api/v1/partners/:partnerId/addresses/:id
   * Elimina una direccion
   */
  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 address belongs to partner
      const existing = await addressService.findById(req.params.id);
      if (!existing || existing.partnerId !== req.params.partnerId) {
        return res.status(404).json({ success: false, error: 'Direccion no encontrada' });
      }

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

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

  return router;
}

export default createPartnerAddressController;
