import { Injectable, Logger, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { PrismaService } from '../../infrastructure/database/prisma.service';

const AGGREGATE_INTERVAL_MS = 60 * 60 * 1000; // hourly

@Injectable()
export class CostAggregationService implements OnModuleInit, OnModuleDestroy {
  private readonly logger = new Logger(CostAggregationService.name);
  private timer: ReturnType<typeof setInterval> | null = null;

  constructor(private readonly prisma: PrismaService) {}

  async onModuleInit() {
    try {
      await this.aggregateToday();
    } catch (err: any) {
      this.logger.warn(`Initial cost aggregation failed: ${err.message}`);
    }
    this.timer = setInterval(() => this.aggregateToday().catch(err => this.logger.warn(`Hourly cost aggregation failed: ${err.message}`)), AGGREGATE_INTERVAL_MS);
  }

  onModuleDestroy() {
    if (this.timer) clearInterval(this.timer);
  }

  /** Aggregate today's AiUsageLog into CostDailySummary */
  async aggregateToday(): Promise<void> {
    const today = new Date(); today.setHours(0, 0, 0, 0);
    const tomorrow = new Date(today); tomorrow.setDate(tomorrow.getDate() + 1);

    const logs = await this.prisma.aiUsageLog.findMany({
      where: { createdAt: { gte: today, lt: tomorrow }, success: true },
      select: { provider: true, model: true, inputTokens: true, outputTokens: true, estimatedCost: true },
    });

    if (logs.length === 0) return;

    // Group by provider+model
    const groups: Record<string, { calls: number; tokens: number; cost: number }> = {};
    for (const l of logs) {
      const key = `${l.provider}|${l.model}`;
      if (!groups[key]) groups[key] = { calls: 0, tokens: 0, cost: 0 };
      groups[key].calls++;
      groups[key].tokens += l.inputTokens + l.outputTokens;
      groups[key].cost += l.estimatedCost;
    }

    // Upsert into CostDailySummary
    for (const [key, data] of Object.entries(groups)) {
      const [provider, model] = key.split('|');
      await this.prisma.costDailySummary.upsert({
        where: { date_provider_model: { date: today, provider, model } },
        update: { calls: data.calls, tokens: data.tokens, cost: data.cost },
        create: { date: today, provider, model, calls: data.calls, tokens: data.tokens, cost: data.cost },
      });
    }

    this.logger.log(`Aggregated ${logs.length} AI calls into CostDailySummary`);
  }

  /** Get cost report by time range */
  async getReport(days = 30) {
    const since = new Date(Date.now() - days * 86400000);

    const [byProvider, byModel, dailyTrend] = await Promise.all([
      this.prisma.costDailySummary.groupBy({
        by: ['provider'],
        where: { date: { gte: since } },
        _sum: { calls: true, tokens: true, cost: true },
      }),
      this.prisma.costDailySummary.groupBy({
        by: ['model'],
        where: { date: { gte: since } },
        _sum: { calls: true, tokens: true, cost: true },
      }),
      this.prisma.costDailySummary.findMany({
        where: { date: { gte: since } },
        orderBy: { date: 'asc' },
      }),
    ]);

    const totalCost = byProvider.reduce((s, p) => s + (p._sum.cost || 0), 0);
    const totalCalls = byProvider.reduce((s, p) => s + (p._sum.calls || 0), 0);

    return {
      period: `${days} days`,
      totalCost: totalCost.toFixed(4),
      totalCalls,
      byProvider: byProvider.map(p => ({
        provider: p.provider,
        calls: p._sum.calls || 0,
        tokens: p._sum.tokens || 0,
        cost: (p._sum.cost || 0).toFixed(4),
      })),
      byModel: byModel.map(m => ({
        model: m.model,
        calls: m._sum.calls || 0,
        tokens: m._sum.tokens || 0,
        cost: (m._sum.cost || 0).toFixed(4),
      })),
      dailyTrend: dailyTrend.map(d => ({
        date: d.date.toISOString().slice(0, 10),
        provider: d.provider,
        model: d.model,
        calls: d.calls,
        cost: d.cost,
      })),
    };
  }

  /** Generate CSV export string */
  async exportCsv(days = 30): Promise<string> {
    const since = new Date(Date.now() - days * 86400000);
    const rows = await this.prisma.costDailySummary.findMany({
      where: { date: { gte: since } },
      orderBy: { date: 'asc' },
    });

    const header = 'date,provider,model,calls,tokens,cost';
    const lines = rows.map(r => `${r.date.toISOString().slice(0,10)},${r.provider},${r.model},${r.calls},${r.tokens},${r.cost}`);
    return [header, ...lines].join('\n');
  }
}
