import { PrismaClient } from '@prisma/client';
import logger from '../utils/logger';
import { systemConfigService } from './systemConfigService';

const prisma = new PrismaClient();

export interface QuotaPeriod {
  periodStart: Date;
  periodEnd: Date;
}

export interface QuotaInfo {
  allocatedSeconds: number;
  usedSeconds: number;
  remainingSeconds: number;
  periodStart: Date;
  periodEnd: Date;
  baseAllocatedSeconds?: number;
  bonusAllocatedSeconds?: number;
  bonusExpiresAt?: Date | null;
}

export class QuotaService {
  /**
   * Obtiene el período actual basado en el día ancla (día 1 por defecto)
   */
  static getCurrentPeriod(now = new Date(), anchorDay = 1): QuotaPeriod {
    const start = new Date(now);
    start.setUTCDate(anchorDay);
    start.setUTCHours(0, 0, 0, 0);
    
    if (now < start) {
      // mover al mes anterior
      start.setUTCMonth(start.getUTCMonth() - 1);
    }
    
    const end = new Date(start);
    end.setUTCMonth(end.getUTCMonth() + 1);
    
    return { periodStart: start, periodEnd: end };
  }

  /**
   * Calcula segundos restantes para un usuario en el período actual
   */
  static async getRemainingSeconds(userId: string, anchorDay = 1): Promise<QuotaInfo> {
    try {
      const { periodStart, periodEnd } = this.getCurrentPeriod(new Date(), anchorDay);

      logger.info('📊 Calculating quota for user', {
        userId,
        periodStart: periodStart.toISOString(),
        periodEnd: periodEnd.toISOString()
      });

      // Sumar asignaciones basadas en grants (BASE + BONUS) para período actual
      const now = new Date();
      const grants = await prisma.quotaGrant.findMany({
        where: {
          userId,
          periodStart,
          OR: [
            { expiresAt: null },
            { expiresAt: { gte: now } },
          ],
        },
        select: { seconds: true, type: true, expiresAt: true },
      });

      const baseAllocatedSeconds = grants
        .filter(g => g.type === 'BASE')
        .reduce((sum, g) => sum + (g.seconds || 0), 0);
      
      const bonusAllocatedSeconds = grants
        .filter(g => g.type === 'BONUS')
        .reduce((sum, g) => sum + (g.seconds || 0), 0);

      // Determinar caducidad del bonus
      let bonusExpiresAt: Date | null = null;
      const activeBonusExpiries = grants
        .filter(g => g.type === 'BONUS')
        .map(g => g.expiresAt ?? periodEnd);
      
      if (activeBonusExpiries.length > 0) {
        bonusExpiresAt = activeBonusExpiries.reduce((max, d) => (d > max ? d : max), new Date(0));
      }

      let allocatedSeconds = baseAllocatedSeconds + bonusAllocatedSeconds;

      // Sumar ledger de uso dentro del período
      const usage = await prisma.usageLedger.aggregate({
        _sum: { seconds: true },
        where: {
          userId,
          createdAt: {
            gte: periodStart,
            lt: periodEnd,
          },
        },
      });

      const usedSeconds = Math.max(0, usage._sum.seconds ?? 0);
      const remainingSeconds = Math.max(0, allocatedSeconds - usedSeconds);

      logger.info('✅ Quota calculation completed', {
        userId,
        allocatedSeconds,
        usedSeconds,
        remainingSeconds,
        periodStart,
        periodEnd,
        baseAllocatedSeconds,
        bonusAllocatedSeconds,
        bonusExpiresAt,
      });

      return { 
        allocatedSeconds, 
        usedSeconds, 
        remainingSeconds, 
        periodStart, 
        periodEnd, 
        baseAllocatedSeconds, 
        bonusAllocatedSeconds, 
        bonusExpiresAt 
      };
    } catch (error: any) {
      logger.error('❌ Error calculating quota for user', {
        userId,
        error: error.message,
        stack: error.stack
      });
      throw error;
    }
  }

  /**
   * Establece la cuota base para un usuario en el período actual
   */
  static async setBaseQuota(userId: string, allocatedSeconds: number, reason?: string): Promise<any> {
    try {
      const { periodStart, periodEnd } = this.getCurrentPeriod();
      
      const baseGrant = await prisma.quotaGrant.upsert({
        where: {
          userId_periodStart_type: { userId, periodStart, type: 'BASE' }
        },
        create: {
          userId,
          periodStart,
          periodEnd,
          seconds: allocatedSeconds,
          type: 'BASE',
          reason: reason || 'Base monthly allocation',
        },
        update: {
          seconds: allocatedSeconds,
          periodEnd,
          reason,
        },
      });

      logger.info('📝 Base quota set for user', {
        userId,
        allocatedSeconds,
        periodStart,
        periodEnd,
        reason
      });

      return baseGrant;
    } catch (error: any) {
      logger.error('❌ Error setting base quota for user', {
        userId,
        allocatedSeconds,
        error: error.message
      });
      throw error;
    }
  }

  /**
   * Establece cuota bonus para un usuario con fecha de caducidad opcional
   */
  static async setBonusQuota(
    userId: string, 
    bonusSeconds: number, 
    expiresAt?: Date, 
    reason?: string
  ): Promise<any> {
    try {
      const { periodStart, periodEnd } = this.getCurrentPeriod();
      
      let bonusGrant = null;
      if (bonusSeconds > 0) {
        bonusGrant = await prisma.quotaGrant.upsert({
          where: {
            userId_periodStart_type: { userId, periodStart, type: 'BONUS' }
          },
          create: {
            userId,
            periodStart,
            periodEnd,
            seconds: bonusSeconds,
            type: 'BONUS',
            reason: reason || 'Bonus allocation',
            expiresAt,
          },
          update: {
            seconds: bonusSeconds,
            periodEnd,
            expiresAt,
            reason,
          },
        });
      } else {
        // Si es 0, eliminar BONUS existente del período
        try {
          await prisma.quotaGrant.delete({
            where: {
              userId_periodStart_type: { userId, periodStart, type: 'BONUS' }
            },
          });
        } catch (e) {
          // ignorar si no se encuentra
        }
      }

      logger.info('🎁 Bonus quota set for user', {
        userId,
        bonusSeconds,
        expiresAt,
        periodStart,
        periodEnd,
        reason
      });

      return bonusGrant;
    } catch (error: any) {
      logger.error('❌ Error setting bonus quota for user', {
        userId,
        bonusSeconds,
        error: error.message
      });
      throw error;
    }
  }

  /**
   * Registra uso en el ledger
   */
  static async recordUsage(
    userId: string,
    seconds: number,
    reason?: string,
    source?: string,
    relatedConversationId?: string
  ): Promise<any> {
    try {
      // Upsert on relatedConversationId (unique): if 'close' and 'error' both fire for
      // the same disconnect, endConversation() can end up recording usage for the same
      // conversation twice — update the existing row instead of failing on the second write.
      const usageEntry = relatedConversationId
        ? await prisma.usageLedger.upsert({
            where: { relatedConversationId },
            create: { userId, seconds, reason, source, relatedConversationId },
            update: { seconds, reason, source },
          })
        : await prisma.usageLedger.create({
            data: { userId, seconds, reason, source, relatedConversationId },
          });

      logger.info('📝 Usage recorded in ledger', {
        userId,
        seconds,
        reason,
        source,
        relatedConversationId
      });

      return usageEntry;
    } catch (error: any) {
      logger.error('❌ Error recording usage', {
        userId,
        seconds,
        error: error.message
      });
      throw error;
    }
  }

  /**
   * Obtener información de timer para un usuario usando cuotas mensuales
   * Esto integra el sistema de cuotas con el timer visual
   */
  static async getTimerInfo(userId: string): Promise<{
    remainingSeconds: number;
    totalQuotaSeconds: number;
    usedSeconds: number;
    isTimerEnabled: boolean;
    isQuotaExhausted: boolean;
  }> {
    try {
      logger.info('⏰ Getting timer info for user', { userId });

      const quotaManagementEnabled = await systemConfigService.isQuotaManagementEnabledFresh();
      if (!quotaManagementEnabled) {
        const disabledInfo = {
          remainingSeconds: 0,
          totalQuotaSeconds: 0,
          usedSeconds: 0,
          isTimerEnabled: false,
          isQuotaExhausted: false
        };
        logger.info('⏰ Quota management disabled – timer off, no limit', { userId });
        return disabledInfo;
      }

      const quotaInfo = await this.getRemainingSeconds(userId);
      
      const timerInfo = {
        remainingSeconds: Math.max(0, quotaInfo.remainingSeconds),
        totalQuotaSeconds: quotaInfo.allocatedSeconds,
        usedSeconds: quotaInfo.usedSeconds,
        isTimerEnabled: quotaInfo.allocatedSeconds > 0,
        isQuotaExhausted: quotaInfo.remainingSeconds <= 0
      };
      
      logger.info('✅ Timer info calculated', {
        userId,
        timerInfo
      });

      return timerInfo;
    } catch (error: any) {
      logger.error('❌ Error getting timer info', {
        userId,
        error: error.message
      });
      throw error;
    }
  }

  /**
   * Verificar si un usuario puede usar un bot basado en cuotas
   * (Integración con sistema de timer)
   */
  static async canUserUseBot(userId: string): Promise<{
    canUse: boolean;
    remainingSeconds: number;
    reason?: string;
  }> {
    try {
      logger.info('🔍 Checking if user can use bot', { userId });

      const quotaManagementEnabled = await systemConfigService.isQuotaManagementEnabledFresh();
      if (!quotaManagementEnabled) {
        logger.info('🔍 Quota management disabled – user can use bot without limit', { userId });
        return { canUse: true, remainingSeconds: 0 };
      }
      
      const quotaInfo = await this.getRemainingSeconds(userId);
      
      const canUse = quotaInfo.remainingSeconds > 0;
      const result = {
        canUse,
        remainingSeconds: Math.max(0, quotaInfo.remainingSeconds),
        reason: canUse ? undefined : 'Monthly quota exhausted'
      };
      
      logger.info('✅ User bot access check completed', {
        userId,
        canUse,
        remainingSeconds: result.remainingSeconds,
        reason: result.reason
      });

      return result;
    } catch (error: any) {
      logger.error('❌ Error checking user bot access', {
        userId,
        error: error.message
      });
      throw error;
    }
  }

}
