import { Response, NextFunction } from 'express';
import { z } from 'zod';
import { apiKeysService, CreateApiKeyDto, UpdateApiKeyDto, ApiKeyFilters } from './apiKeys.service.js';
import { AuthenticatedRequest, ValidationError, ApiResponse } from '../../shared/types/index.js';

// ============================================================================
// VALIDATION SCHEMAS
// ============================================================================

const createApiKeySchema = z.object({
  name: z.string().min(1, 'Nombre requerido').max(255),
  scope: z.string().max(100).optional(),
  allowed_ips: z.array(z.string().ip()).optional(),
  expiration_days: z.number().int().positive().max(365).optional(),
});

const updateApiKeySchema = z.object({
  name: z.string().min(1).max(255).optional(),
  scope: z.string().max(100).nullable().optional(),
  allowed_ips: z.array(z.string().ip()).nullable().optional(),
  expiration_date: z.string().datetime().nullable().optional(),
  is_active: z.boolean().optional(),
});

const listApiKeysSchema = z.object({
  user_id: z.string().uuid().optional(),
  is_active: z.enum(['true', 'false']).optional(),
  scope: z.string().optional(),
});

// ============================================================================
// CONTROLLER
// ============================================================================

class ApiKeysController {
  /**
   * Create a new API key
   * POST /api/auth/api-keys
   */
  async create(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const validation = createApiKeySchema.safeParse(req.body);
      if (!validation.success) {
        throw new ValidationError('Datos inválidos', validation.error.errors);
      }

      const dto: CreateApiKeyDto = {
        ...validation.data,
        user_id: req.user!.userId,
        tenant_id: req.user!.tenantId,
      };

      const result = await apiKeysService.create(dto);

      const response: ApiResponse = {
        success: true,
        data: result,
        message: 'API key creada exitosamente. Guarde la clave, no podrá verla de nuevo.',
      };

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

  /**
   * List API keys for the current user
   * GET /api/auth/api-keys
   */
  async list(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const validation = listApiKeysSchema.safeParse(req.query);
      if (!validation.success) {
        throw new ValidationError('Parámetros inválidos', validation.error.errors);
      }

      const filters: ApiKeyFilters = {
        tenant_id: req.user!.tenantId,
        // By default, only show user's own keys unless admin
        user_id: validation.data.user_id || req.user!.userId,
      };

      // Admins can view all keys in tenant
      if (validation.data.user_id && req.user!.roles.includes('admin')) {
        filters.user_id = validation.data.user_id;
      }

      if (validation.data.is_active !== undefined) {
        filters.is_active = validation.data.is_active === 'true';
      }

      if (validation.data.scope) {
        filters.scope = validation.data.scope;
      }

      const apiKeys = await apiKeysService.findAll(filters);

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

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

  /**
   * Get a specific API key
   * GET /api/auth/api-keys/:id
   */
  async getById(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;

      const apiKey = await apiKeysService.findById(id, req.user!.tenantId);

      if (!apiKey) {
        const response: ApiResponse = {
          success: false,
          error: 'API key no encontrada',
        };
        res.status(404).json(response);
        return;
      }

      // Check ownership (unless admin)
      if (apiKey.user_id !== req.user!.userId && !req.user!.roles.includes('admin')) {
        const response: ApiResponse = {
          success: false,
          error: 'No tiene permisos para ver esta API key',
        };
        res.status(403).json(response);
        return;
      }

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

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

  /**
   * Update an API key
   * PATCH /api/auth/api-keys/:id
   */
  async update(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;

      const validation = updateApiKeySchema.safeParse(req.body);
      if (!validation.success) {
        throw new ValidationError('Datos inválidos', validation.error.errors);
      }

      // Check ownership first
      const existing = await apiKeysService.findById(id, req.user!.tenantId);
      if (!existing) {
        const response: ApiResponse = {
          success: false,
          error: 'API key no encontrada',
        };
        res.status(404).json(response);
        return;
      }

      if (existing.user_id !== req.user!.userId && !req.user!.roles.includes('admin')) {
        const response: ApiResponse = {
          success: false,
          error: 'No tiene permisos para modificar esta API key',
        };
        res.status(403).json(response);
        return;
      }

      const dto: UpdateApiKeyDto = {
        name: validation.data.name,
        scope: validation.data.scope ?? undefined,
        allowed_ips: validation.data.allowed_ips ?? undefined,
        is_active: validation.data.is_active,
        expiration_date: validation.data.expiration_date
          ? new Date(validation.data.expiration_date)
          : validation.data.expiration_date === null
          ? null
          : undefined,
      };

      const updated = await apiKeysService.update(id, req.user!.tenantId, dto);

      const response: ApiResponse = {
        success: true,
        data: updated,
        message: 'API key actualizada',
      };

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

  /**
   * Revoke an API key (soft delete)
   * POST /api/auth/api-keys/:id/revoke
   */
  async revoke(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;

      // Check ownership first
      const existing = await apiKeysService.findById(id, req.user!.tenantId);
      if (!existing) {
        const response: ApiResponse = {
          success: false,
          error: 'API key no encontrada',
        };
        res.status(404).json(response);
        return;
      }

      if (existing.user_id !== req.user!.userId && !req.user!.roles.includes('admin')) {
        const response: ApiResponse = {
          success: false,
          error: 'No tiene permisos para revocar esta API key',
        };
        res.status(403).json(response);
        return;
      }

      await apiKeysService.revoke(id, req.user!.tenantId);

      const response: ApiResponse = {
        success: true,
        message: 'API key revocada exitosamente',
      };

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

  /**
   * Delete an API key permanently
   * DELETE /api/auth/api-keys/:id
   */
  async delete(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;

      // Check ownership first
      const existing = await apiKeysService.findById(id, req.user!.tenantId);
      if (!existing) {
        const response: ApiResponse = {
          success: false,
          error: 'API key no encontrada',
        };
        res.status(404).json(response);
        return;
      }

      if (existing.user_id !== req.user!.userId && !req.user!.roles.includes('admin')) {
        const response: ApiResponse = {
          success: false,
          error: 'No tiene permisos para eliminar esta API key',
        };
        res.status(403).json(response);
        return;
      }

      await apiKeysService.delete(id, req.user!.tenantId);

      const response: ApiResponse = {
        success: true,
        message: 'API key eliminada permanentemente',
      };

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

  /**
   * Regenerate an API key (invalidates old key, creates new)
   * POST /api/auth/api-keys/:id/regenerate
   */
  async regenerate(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;

      // Check ownership first
      const existing = await apiKeysService.findById(id, req.user!.tenantId);
      if (!existing) {
        const response: ApiResponse = {
          success: false,
          error: 'API key no encontrada',
        };
        res.status(404).json(response);
        return;
      }

      if (existing.user_id !== req.user!.userId && !req.user!.roles.includes('admin')) {
        const response: ApiResponse = {
          success: false,
          error: 'No tiene permisos para regenerar esta API key',
        };
        res.status(403).json(response);
        return;
      }

      const result = await apiKeysService.regenerate(id, req.user!.tenantId);

      const response: ApiResponse = {
        success: true,
        data: result,
        message: 'API key regenerada. Guarde la nueva clave, no podrá verla de nuevo.',
      };

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

export const apiKeysController = new ApiKeysController();
