/**
 * Partner Bank Account Controller
 * API endpoints para gestion de cuentas bancarias de partners
 *
 * @module Partners
 * @prefix /api/v1/partners/:partnerId/bank-accounts
 */

import { Router, Request, Response, NextFunction } from 'express';
import { DataSource } from 'typeorm';
import {
  PartnerBankAccountService,
  CreatePartnerBankAccountDto,
  UpdatePartnerBankAccountDto,
  ServiceContext,
} from '../services/partner-bank-account.service';
import { PartnerService } from '../services/partner.service';

export function createPartnerBankAccountController(dataSource: DataSource): Router {
  const router = Router({ mergeParams: true });
  const bankAccountService = new PartnerBankAccountService(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/bank-accounts
   * Lista todas las cuentas bancarias 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 accounts = await bankAccountService.findByPartnerId(req.params.partnerId);
      return res.json({ success: true, data: accounts, count: accounts.length });
    } catch (error) {
      return next(error);
    }
  });

  /**
   * GET /api/v1/partners/:partnerId/bank-accounts/default
   * Obtiene la cuenta bancaria predeterminada
   */
  router.get('/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;

      const account = await bankAccountService.findDefaultAccount(req.params.partnerId);
      if (!account) {
        return res.status(404).json({ success: false, error: 'No hay cuenta predeterminada' });
      }

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

  /**
   * GET /api/v1/partners/:partnerId/bank-accounts/verified
   * Lista cuentas bancarias verificadas
   */
  router.get('/verified', 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 accounts = await bankAccountService.findVerifiedAccounts(req.params.partnerId);
      return res.json({ success: true, data: accounts, count: accounts.length });
    } catch (error) {
      return next(error);
    }
  });

  /**
   * GET /api/v1/partners/:partnerId/bank-accounts/summary
   * Obtiene resumen de cuentas bancarias
   */
  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 bankAccountService.getAccountSummary(req.params.partnerId);
      return res.json({ success: true, data: summary });
    } catch (error) {
      return next(error);
    }
  });

  /**
   * GET /api/v1/partners/:partnerId/bank-accounts/:id
   * Obtiene una cuenta bancaria 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 account = await bankAccountService.findById(req.params.id);
      if (!account || account.partnerId !== req.params.partnerId) {
        return res.status(404).json({ success: false, error: 'Cuenta bancaria no encontrada' });
      }

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

  /**
   * GET /api/v1/partners/:partnerId/bank-accounts/:id/masked
   * Obtiene el numero de cuenta enmascarado
   */
  router.get('/:id/masked', 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 account = await bankAccountService.findById(req.params.id);
      if (!account || account.partnerId !== req.params.partnerId) {
        return res.status(404).json({ success: false, error: 'Cuenta bancaria no encontrada' });
      }

      const masked = await bankAccountService.getMaskedAccountNumber(req.params.id);
      return res.json({ success: true, data: { maskedAccountNumber: masked } });
    } catch (error) {
      return next(error);
    }
  });

  /**
   * POST /api/v1/partners/:partnerId/bank-accounts
   * Crea una nueva cuenta bancaria
   */
  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: CreatePartnerBankAccountDto = {
        ...req.body,
        partnerId: req.params.partnerId,
      };

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

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

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

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

      const dto: UpdatePartnerBankAccountDto = req.body;
      const account = await bankAccountService.update(ctx, req.params.id, dto);

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

      return res.json({ success: true, data: account });
    } catch (error: any) {
      if (error.message?.includes('CLABE') || error.message?.includes('already exists')) {
        return res.status(400).json({ success: false, error: error.message });
      }
      return next(error);
    }
  });

  /**
   * PATCH /api/v1/partners/:partnerId/bank-accounts/:id/default
   * Establece una cuenta 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 account belongs to partner
      const existing = await bankAccountService.findById(req.params.id);
      if (!existing || existing.partnerId !== req.params.partnerId) {
        return res.status(404).json({ success: false, error: 'Cuenta bancaria no encontrada' });
      }

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

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

  /**
   * PATCH /api/v1/partners/:partnerId/bank-accounts/:id/verify
   * Verifica una cuenta bancaria
   */
  router.patch('/:id/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;

      // Verify account belongs to partner
      const existing = await bankAccountService.findById(req.params.id);
      if (!existing || existing.partnerId !== req.params.partnerId) {
        return res.status(404).json({ success: false, error: 'Cuenta bancaria no encontrada' });
      }

      const account = await bankAccountService.verify(ctx, req.params.id);
      if (!account) {
        return res.status(404).json({ success: false, error: 'Cuenta bancaria no encontrada' });
      }

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

  /**
   * PATCH /api/v1/partners/:partnerId/bank-accounts/:id/unverify
   * Quita la verificacion de una cuenta bancaria
   */
  router.patch('/:id/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;

      // Verify account belongs to partner
      const existing = await bankAccountService.findById(req.params.id);
      if (!existing || existing.partnerId !== req.params.partnerId) {
        return res.status(404).json({ success: false, error: 'Cuenta bancaria no encontrada' });
      }

      const account = await bankAccountService.unverify(ctx, req.params.id);
      if (!account) {
        return res.status(404).json({ success: false, error: 'Cuenta bancaria no encontrada' });
      }

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

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

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

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

  return router;
}

export default createPartnerBankAccountController;
