/**
 * UserBranchAssignment Controller
 * API endpoints para gestión de asignaciones de usuarios a sucursales
 *
 * @module Branches
 * @prefix /api/v1/branches/:branchId/assignments
 * @prefix /api/v1/user-branch-assignments (for user-centric operations)
 */

import { Router, Request, Response, NextFunction } from 'express';
import {
  UserBranchAssignmentService,
  CreateUserBranchAssignmentDto,
  UpdateUserBranchAssignmentDto,
} from '../services/user-branch-assignment.service';
import { ServiceContext } from '../services/branch.service';

const router = Router({ mergeParams: true });
const assignmentService = new UserBranchAssignmentService();

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

/**
 * GET /api/v1/branches/:branchId/assignments
 * Lista todas las asignaciones de usuarios a una sucursal
 */
router.get('/', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const { branchId } = req.params;
    const { assignmentType, branchRole, isActive } = req.query;

    const assignments = await assignmentService.findAll(getContext(req), {
      branchId,
      assignmentType: assignmentType as any,
      branchRole: branchRole as any,
      isActive: isActive === 'true' ? true : isActive === 'false' ? false : undefined,
    });

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

/**
 * GET /api/v1/branches/:branchId/assignments/managers
 * Obtiene los gerentes de una sucursal
 */
router.get('/managers', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const { branchId } = req.params;
    const managers = await assignmentService.getBranchManagers(getContext(req), branchId);

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

/**
 * GET /api/v1/branches/:branchId/assignments/statistics
 * Obtiene estadísticas de asignaciones
 */
router.get('/statistics', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const stats = await assignmentService.getAssignmentStatistics(getContext(req));
    return res.json({ success: true, data: stats });
  } catch (error) {
    return next(error);
  }
});

/**
 * GET /api/v1/branches/:branchId/assignments/check-user/:userId
 * Verifica si un usuario está asignado a una sucursal
 */
router.get('/check-user/:userId', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const { branchId, userId } = req.params;
    const isAssigned = await assignmentService.isUserAssignedToBranch(
      getContext(req),
      userId,
      branchId
    );

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

/**
 * GET /api/v1/branches/:branchId/assignments/:id
 * Obtiene una asignación específica
 */
router.get('/:id', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const assignment = await assignmentService.findById(getContext(req), req.params.id);
    if (!assignment) {
      return res.status(404).json({ error: 'Asignación no encontrada' });
    }

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

/**
 * POST /api/v1/branches/:branchId/assignments
 * Crea una nueva asignación de usuario a sucursal
 */
router.post('/', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const { branchId } = req.params;
    const data: CreateUserBranchAssignmentDto = {
      ...req.body,
      branchId,
    };

    // Validate required fields
    if (!data.userId) {
      return res.status(400).json({ error: 'userId es requerido' });
    }

    const assignment = await assignmentService.create(getContext(req), data);
    return res.status(201).json({ success: true, data: assignment });
  } catch (error) {
    return next(error);
  }
});

/**
 * PATCH /api/v1/branches/:branchId/assignments/:id
 * Actualiza una asignación
 */
router.patch('/:id', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const data: UpdateUserBranchAssignmentDto = req.body;
    const assignment = await assignmentService.update(getContext(req), req.params.id, data);

    if (!assignment) {
      return res.status(404).json({ error: 'Asignación no encontrada' });
    }

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

/**
 * POST /api/v1/branches/:branchId/assignments/:id/set-primary
 * Establece una asignación como primaria
 */
router.post('/:id/set-primary', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const assignment = await assignmentService.setAsPrimary(getContext(req), req.params.id);
    if (!assignment) {
      return res.status(404).json({ error: 'Asignación no encontrada' });
    }

    return res.json({
      success: true,
      data: assignment,
      message: 'Asignación establecida como primaria',
    });
  } catch (error) {
    return next(error);
  }
});

/**
 * POST /api/v1/branches/:branchId/assignments/:id/deactivate
 * Desactiva una asignación
 */
router.post('/:id/deactivate', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const assignment = await assignmentService.deactivate(getContext(req), req.params.id);
    if (!assignment) {
      return res.status(404).json({ error: 'Asignación no encontrada' });
    }

    return res.json({ success: true, data: assignment, message: 'Asignación desactivada' });
  } catch (error) {
    return next(error);
  }
});

/**
 * DELETE /api/v1/branches/:branchId/assignments/:id
 * Elimina una asignación
 */
router.delete('/:id', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const deleted = await assignmentService.delete(getContext(req), req.params.id);
    if (!deleted) {
      return res.status(404).json({ error: 'Asignación no encontrada' });
    }

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

export default router;

/**
 * User-centric router for branch assignments
 * Mounted at /api/v1/user-branch-assignments
 */
export const userBranchAssignmentRouter = Router();

/**
 * GET /api/v1/user-branch-assignments/user/:userId
 * Obtiene todas las asignaciones de un usuario
 */
userBranchAssignmentRouter.get(
  '/user/:userId',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      if (!tenantId) {
        return res.status(400).json({ error: 'X-Tenant-Id header required' });
      }

      const { userId } = req.params;
      const assignments = await assignmentService.findByUser(getContext(req), userId);

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

/**
 * GET /api/v1/user-branch-assignments/user/:userId/primary
 * Obtiene la asignación primaria de un usuario
 */
userBranchAssignmentRouter.get(
  '/user/:userId/primary',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      if (!tenantId) {
        return res.status(400).json({ error: 'X-Tenant-Id header required' });
      }

      const { userId } = req.params;
      const assignment = await assignmentService.findPrimaryAssignment(getContext(req), userId);

      if (!assignment) {
        return res.status(404).json({ error: 'No hay asignación primaria para este usuario' });
      }

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

/**
 * GET /api/v1/user-branch-assignments/user/:userId/active
 * Obtiene las asignaciones activas de un usuario
 */
userBranchAssignmentRouter.get(
  '/user/:userId/active',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      if (!tenantId) {
        return res.status(400).json({ error: 'X-Tenant-Id header required' });
      }

      const { userId } = req.params;
      const { asOfDate } = req.query;

      const assignments = await assignmentService.findActiveAssignments(
        getContext(req),
        userId,
        asOfDate ? new Date(asOfDate as string) : undefined
      );

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

/**
 * GET /api/v1/user-branch-assignments/user/:userId/permissions/:branchId
 * Obtiene los permisos de un usuario en una sucursal
 */
userBranchAssignmentRouter.get(
  '/user/:userId/permissions/:branchId',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      if (!tenantId) {
        return res.status(400).json({ error: 'X-Tenant-Id header required' });
      }

      const { userId, branchId } = req.params;
      const permissions = await assignmentService.getUserPermissionsForBranch(
        getContext(req),
        userId,
        branchId
      );

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

/**
 * POST /api/v1/user-branch-assignments/transfer
 * Transfiere un usuario de una sucursal a otra
 */
userBranchAssignmentRouter.post(
  '/transfer',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      if (!tenantId) {
        return res.status(400).json({ error: 'X-Tenant-Id header required' });
      }

      const { userId, fromBranchId, toBranchId, transferPrimary } = req.body;

      if (!userId || !fromBranchId || !toBranchId) {
        return res.status(400).json({
          error: 'userId, fromBranchId y toBranchId son requeridos',
        });
      }

      const assignment = await assignmentService.transferUserToBranch(
        getContext(req),
        userId,
        fromBranchId,
        toBranchId,
        transferPrimary !== false
      );

      if (!assignment) {
        return res.status(400).json({ error: 'No se pudo completar la transferencia' });
      }

      return res.json({
        success: true,
        data: assignment,
        message: 'Usuario transferido exitosamente',
      });
    } catch (error) {
      return next(error);
    }
  }
);
