import { Repository, FindOptionsWhere, Between, LessThanOrEqual, MoreThanOrEqual, In } from 'typeorm';
import {
  Device,
  BiometricCredential,
  DeviceSession,
  DeviceActivityLog,
  BiometricType,
  DevicePlatform,
  ActivityType,
  ActivityStatus,
} from '../entities';

export interface DeviceFilters {
  platform?: DevicePlatform;
  isActive?: boolean;
  biometricEnabled?: boolean;
}

export interface CredentialFilters {
  biometricType?: BiometricType;
  isActive?: boolean;
}

export interface ActivityLogFilters {
  activityType?: ActivityType;
  activityStatus?: ActivityStatus;
  startDate?: Date;
  endDate?: Date;
}

export interface SessionFilters {
  isActive?: boolean;
}

export interface PaginationOptions {
  page?: number;
  limit?: number;
}

export interface EnrollmentStatus {
  userId: string;
  hasEnrollment: boolean;
  totalCredentials: number;
  activeCredentials: number;
  primaryCredential: BiometricCredential | null;
  biometricTypes: BiometricType[];
  devices: {
    deviceId: string;
    deviceName: string;
    credentialsCount: number;
  }[];
}

/**
 * BiometricsService - Servicio de solo lectura para datos biometricos
 *
 * Este servicio proporciona metodos de consulta para dispositivos,
 * credenciales biometricas, sesiones y logs de actividad.
 *
 * La funcionalidad de escritura (crear, modificar, eliminar) se maneja
 * desde los modulos `auth` y `devices`.
 */
export class BiometricsService {
  constructor(
    private readonly deviceRepository: Repository<Device>,
    private readonly credentialRepository: Repository<BiometricCredential>,
    private readonly sessionRepository: Repository<DeviceSession>,
    private readonly activityLogRepository: Repository<DeviceActivityLog>
  ) {}

  // ============================================
  // DEVICES (Solo lectura)
  // ============================================

  /**
   * Obtiene dispositivos de un usuario
   */
  async findDevicesByUserId(
    userId: string,
    filters: DeviceFilters = {},
    pagination: PaginationOptions = {}
  ): Promise<{ data: Device[]; total: number }> {
    const { page = 1, limit = 20 } = pagination;
    const where: FindOptionsWhere<Device> = { userId };

    if (filters.platform) where.platform = filters.platform;
    if (filters.isActive !== undefined) where.isActive = filters.isActive;
    if (filters.biometricEnabled !== undefined) where.biometricEnabled = filters.biometricEnabled;

    const [data, total] = await this.deviceRepository.findAndCount({
      where,
      order: { lastSeenAt: 'DESC' },
      skip: (page - 1) * limit,
      take: limit,
    });

    return { data, total };
  }

  /**
   * Obtiene un dispositivo por ID
   */
  async findDeviceById(deviceId: string, userId?: string): Promise<Device | null> {
    const where: FindOptionsWhere<Device> = { id: deviceId };
    if (userId) where.userId = userId;

    return this.deviceRepository.findOne({
      where,
      relations: ['biometricCredentials', 'sessions'],
    });
  }

  /**
   * Obtiene un dispositivo por UUID
   */
  async findDeviceByUuid(deviceUuid: string, userId: string): Promise<Device | null> {
    return this.deviceRepository.findOne({
      where: { deviceUuid, userId },
      relations: ['biometricCredentials'],
    });
  }

  // ============================================
  // BIOMETRIC CREDENTIALS (Solo lectura)
  // ============================================

  /**
   * Obtiene credenciales biometricas de un usuario
   */
  async findCredentialsByUserId(
    userId: string,
    filters: CredentialFilters = {},
    pagination: PaginationOptions = {}
  ): Promise<{ data: BiometricCredential[]; total: number }> {
    const { page = 1, limit = 20 } = pagination;
    const where: FindOptionsWhere<BiometricCredential> = { userId };

    if (filters.biometricType) where.biometricType = filters.biometricType;
    if (filters.isActive !== undefined) where.isActive = filters.isActive;

    const [data, total] = await this.credentialRepository.findAndCount({
      where,
      order: { createdAt: 'DESC' },
      skip: (page - 1) * limit,
      take: limit,
      relations: ['device'],
    });

    return { data, total };
  }

  /**
   * Obtiene credenciales de un dispositivo
   */
  async findCredentialsByDeviceId(
    deviceId: string,
    userId?: string
  ): Promise<BiometricCredential[]> {
    const where: FindOptionsWhere<BiometricCredential> = { deviceId };
    if (userId) where.userId = userId;

    return this.credentialRepository.find({
      where,
      order: { isPrimary: 'DESC', createdAt: 'DESC' },
    });
  }

  /**
   * Obtiene una credencial por ID
   */
  async findCredentialById(
    credentialId: string,
    userId?: string
  ): Promise<BiometricCredential | null> {
    const where: FindOptionsWhere<BiometricCredential> = { id: credentialId };
    if (userId) where.userId = userId;

    return this.credentialRepository.findOne({
      where,
      relations: ['device'],
    });
  }

  // ============================================
  // ENROLLMENT STATUS (Solo lectura)
  // ============================================

  /**
   * Verifica el estado de enrollment biometrico de un usuario
   */
  async getEnrollmentStatus(userId: string): Promise<EnrollmentStatus> {
    const credentials = await this.credentialRepository.find({
      where: { userId },
      relations: ['device'],
    });

    const devices = await this.deviceRepository.find({
      where: { userId, biometricEnabled: true },
    });

    const activeCredentials = credentials.filter((c) => c.isActive);
    const primaryCredential = credentials.find((c) => c.isPrimary && c.isActive) || null;

    // Obtener tipos biometricos unicos
    const biometricTypes = [...new Set(activeCredentials.map((c) => c.biometricType))];

    // Agrupar credenciales por dispositivo
    const deviceCredentialsMap = new Map<string, { name: string; count: number }>();
    for (const credential of activeCredentials) {
      const device = credential.device;
      if (device) {
        const existing = deviceCredentialsMap.get(device.id);
        if (existing) {
          existing.count++;
        } else {
          deviceCredentialsMap.set(device.id, {
            name: device.deviceName || device.deviceModel || 'Dispositivo desconocido',
            count: 1,
          });
        }
      }
    }

    const devicesList = Array.from(deviceCredentialsMap.entries()).map(([id, data]) => ({
      deviceId: id,
      deviceName: data.name,
      credentialsCount: data.count,
    }));

    return {
      userId,
      hasEnrollment: activeCredentials.length > 0,
      totalCredentials: credentials.length,
      activeCredentials: activeCredentials.length,
      primaryCredential,
      biometricTypes,
      devices: devicesList,
    };
  }

  // ============================================
  // DEVICE SESSIONS (Solo lectura)
  // ============================================

  /**
   * Lista sesiones de un usuario
   */
  async findSessionsByUserId(
    userId: string,
    filters: SessionFilters = {},
    pagination: PaginationOptions = {}
  ): Promise<{ data: DeviceSession[]; total: number }> {
    const { page = 1, limit = 20 } = pagination;
    const where: FindOptionsWhere<DeviceSession> = { userId };

    if (filters.isActive !== undefined) where.isActive = filters.isActive;

    const [data, total] = await this.sessionRepository.findAndCount({
      where,
      order: { issuedAt: 'DESC' },
      skip: (page - 1) * limit,
      take: limit,
      relations: ['device'],
    });

    return { data, total };
  }

  /**
   * Obtiene sesiones activas de un usuario
   */
  async findActiveSessionsByUserId(userId: string): Promise<DeviceSession[]> {
    const now = new Date();
    return this.sessionRepository.find({
      where: {
        userId,
        isActive: true,
        expiresAt: MoreThanOrEqual(now),
      },
      order: { issuedAt: 'DESC' },
      relations: ['device'],
    });
  }

  /**
   * Cuenta sesiones activas de un usuario
   */
  async countActiveSessions(userId: string): Promise<number> {
    const now = new Date();
    return this.sessionRepository.count({
      where: {
        userId,
        isActive: true,
        expiresAt: MoreThanOrEqual(now),
      },
    });
  }

  // ============================================
  // ACTIVITY LOG (Solo lectura)
  // ============================================

  /**
   * Lista actividad de un usuario
   */
  async findActivityByUserId(
    userId: string,
    filters: ActivityLogFilters = {},
    pagination: PaginationOptions = {}
  ): Promise<{ data: DeviceActivityLog[]; total: number }> {
    const { page = 1, limit = 50 } = pagination;
    const where: FindOptionsWhere<DeviceActivityLog> = { userId };

    if (filters.activityType) where.activityType = filters.activityType;
    if (filters.activityStatus) where.activityStatus = filters.activityStatus;

    if (filters.startDate && filters.endDate) {
      where.createdAt = Between(filters.startDate, filters.endDate);
    } else if (filters.startDate) {
      where.createdAt = MoreThanOrEqual(filters.startDate);
    } else if (filters.endDate) {
      where.createdAt = LessThanOrEqual(filters.endDate);
    }

    const [data, total] = await this.activityLogRepository.findAndCount({
      where,
      order: { createdAt: 'DESC' },
      skip: (page - 1) * limit,
      take: limit,
    });

    return { data, total };
  }

  /**
   * Lista actividad de un dispositivo
   */
  async findActivityByDeviceId(
    deviceId: string,
    filters: ActivityLogFilters = {},
    pagination: PaginationOptions = {}
  ): Promise<{ data: DeviceActivityLog[]; total: number }> {
    const { page = 1, limit = 50 } = pagination;
    const where: FindOptionsWhere<DeviceActivityLog> = { deviceId };

    if (filters.activityType) where.activityType = filters.activityType;
    if (filters.activityStatus) where.activityStatus = filters.activityStatus;

    if (filters.startDate && filters.endDate) {
      where.createdAt = Between(filters.startDate, filters.endDate);
    } else if (filters.startDate) {
      where.createdAt = MoreThanOrEqual(filters.startDate);
    } else if (filters.endDate) {
      where.createdAt = LessThanOrEqual(filters.endDate);
    }

    const [data, total] = await this.activityLogRepository.findAndCount({
      where,
      order: { createdAt: 'DESC' },
      skip: (page - 1) * limit,
      take: limit,
    });

    return { data, total };
  }

  /**
   * Obtiene estadisticas de verificaciones biometricas
   */
  async getVerificationStats(
    userId: string,
    startDate?: Date,
    endDate?: Date
  ): Promise<{
    total: number;
    successful: number;
    failed: number;
    blocked: number;
    successRate: number;
  }> {
    const where: FindOptionsWhere<DeviceActivityLog> = {
      userId,
      activityType: 'biometric_auth',
    };

    if (startDate && endDate) {
      where.createdAt = Between(startDate, endDate);
    } else if (startDate) {
      where.createdAt = MoreThanOrEqual(startDate);
    } else if (endDate) {
      where.createdAt = LessThanOrEqual(endDate);
    }

    const logs = await this.activityLogRepository.find({ where });

    const total = logs.length;
    const successful = logs.filter((l) => l.activityStatus === 'success').length;
    const failed = logs.filter((l) => l.activityStatus === 'failed').length;
    const blocked = logs.filter((l) => l.activityStatus === 'blocked').length;
    const successRate = total > 0 ? (successful / total) * 100 : 0;

    return {
      total,
      successful,
      failed,
      blocked,
      successRate: Math.round(successRate * 100) / 100,
    };
  }
}
