import { Response, NextFunction } from 'express';
import { z } from 'zod';
import { rolesService } from './roles.service.js';
import { ApiResponse, AuthenticatedRequest, ValidationError, PaginationParams } from '../../shared/types/index.js';

// Validation schemas
const createRoleSchema = z.object({
  name: z.string().min(2, 'El nombre debe tener al menos 2 caracteres'),
  code: z.string()
    .min(2, 'El código debe tener al menos 2 caracteres')
    .regex(/^[a-z_]+$/, 'El código debe contener solo letras minúsculas y guiones bajos'),
  description: z.string().optional(),
  color: z.string().regex(/^#[0-9A-Fa-f]{6}$/, 'Color debe ser formato hexadecimal (#RRGGBB)').optional(),
  permissionIds: z.array(z.string().uuid()).optional(),
});

const updateRoleSchema = z.object({
  name: z.string().min(2).optional(),
  description: z.string().optional(),
  color: z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional(),
});

const assignPermissionsSchema = z.object({
  permissionIds: z.array(z.string().uuid('ID de permiso inválido')),
});

const addPermissionSchema = z.object({
  permissionId: z.string().uuid('ID de permiso inválido'),
});

export class RolesController {
  /**
   * GET /roles - List all roles for tenant
   */
  async findAll(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.user!.tenantId;
      const page = parseInt(req.query.page as string) || 1;
      const limit = Math.min(parseInt(req.query.limit as string) || 20, 100);
      const sortBy = req.query.sortBy as string || 'name';
      const sortOrder = (req.query.sortOrder as 'asc' | 'desc') || 'asc';

      const params: PaginationParams = { page, limit, sortBy, sortOrder };
      const result = await rolesService.findAll(tenantId, params);

      const response: ApiResponse = {
        success: true,
        data: result.roles,
        meta: {
          page,
          limit,
          total: result.total,
          totalPages: Math.ceil(result.total / limit),
        },
      };

      res.json(response);
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /roles/system - Get system roles
   */
  async getSystemRoles(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.user!.tenantId;
      const roles = await rolesService.getSystemRoles(tenantId);

      const response: ApiResponse = {
        success: true,
        data: roles,
      };

      res.json(response);
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /roles/:id - Get role by ID
   */
  async findById(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.user!.tenantId;
      const roleId = req.params.id;

      const role = await rolesService.findById(tenantId, roleId);

      const response: ApiResponse = {
        success: true,
        data: role,
      };

      res.json(response);
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /roles - Create new role
   */
  async create(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const validation = createRoleSchema.safeParse(req.body);
      if (!validation.success) {
        throw new ValidationError('Datos inválidos', validation.error.errors);
      }

      const tenantId = req.user!.tenantId;
      const createdBy = req.user!.userId;

      const role = await rolesService.create(tenantId, validation.data, createdBy);

      const response: ApiResponse = {
        success: true,
        data: role,
        message: 'Rol creado exitosamente',
      };

      res.status(201).json(response);
    } catch (error) {
      next(error);
    }
  }

  /**
   * PUT /roles/:id - Update role
   */
  async update(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const validation = updateRoleSchema.safeParse(req.body);
      if (!validation.success) {
        throw new ValidationError('Datos inválidos', validation.error.errors);
      }

      const tenantId = req.user!.tenantId;
      const roleId = req.params.id;
      const updatedBy = req.user!.userId;

      const role = await rolesService.update(tenantId, roleId, validation.data, updatedBy);

      const response: ApiResponse = {
        success: true,
        data: role,
        message: 'Rol actualizado exitosamente',
      };

      res.json(response);
    } catch (error) {
      next(error);
    }
  }

  /**
   * DELETE /roles/:id - Soft delete role
   */
  async delete(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.user!.tenantId;
      const roleId = req.params.id;
      const deletedBy = req.user!.userId;

      await rolesService.delete(tenantId, roleId, deletedBy);

      const response: ApiResponse = {
        success: true,
        message: 'Rol eliminado exitosamente',
      };

      res.json(response);
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /roles/:id/permissions - Get role permissions
   */
  async getPermissions(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.user!.tenantId;
      const roleId = req.params.id;

      const permissions = await rolesService.getRolePermissions(tenantId, roleId);

      const response: ApiResponse = {
        success: true,
        data: permissions,
      };

      res.json(response);
    } catch (error) {
      next(error);
    }
  }

  /**
   * PUT /roles/:id/permissions - Replace all permissions for a role
   */
  async assignPermissions(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const validation = assignPermissionsSchema.safeParse(req.body);
      if (!validation.success) {
        throw new ValidationError('Datos inválidos', validation.error.errors);
      }

      const tenantId = req.user!.tenantId;
      const roleId = req.params.id;
      const updatedBy = req.user!.userId;

      const role = await rolesService.assignPermissions(
        tenantId,
        roleId,
        validation.data.permissionIds,
        updatedBy
      );

      const response: ApiResponse = {
        success: true,
        data: role,
        message: 'Permisos actualizados exitosamente',
      };

      res.json(response);
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /roles/:id/permissions - Add single permission to role
   */
  async addPermission(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const validation = addPermissionSchema.safeParse(req.body);
      if (!validation.success) {
        throw new ValidationError('Datos inválidos', validation.error.errors);
      }

      const tenantId = req.user!.tenantId;
      const roleId = req.params.id;
      const updatedBy = req.user!.userId;

      const role = await rolesService.addPermission(
        tenantId,
        roleId,
        validation.data.permissionId,
        updatedBy
      );

      const response: ApiResponse = {
        success: true,
        data: role,
        message: 'Permiso agregado exitosamente',
      };

      res.json(response);
    } catch (error) {
      next(error);
    }
  }

  /**
   * DELETE /roles/:id/permissions/:permissionId - Remove permission from role
   */
  async removePermission(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.user!.tenantId;
      const roleId = req.params.id;
      const permissionId = req.params.permissionId;
      const updatedBy = req.user!.userId;

      const role = await rolesService.removePermission(tenantId, roleId, permissionId, updatedBy);

      const response: ApiResponse = {
        success: true,
        data: role,
        message: 'Permiso removido exitosamente',
      };

      res.json(response);
    } catch (error) {
      next(error);
    }
  }
}

export const rolesController = new RolesController();
