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

@Injectable()
export class UserAiQuotaService {
  constructor(private readonly prisma: PrismaService) {}

  /** Get today's date truncated to midnight UTC */
  private today(): Date {
    const d = new Date();
    return new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()));
  }

  /** Read-only check: throws if user is over daily job or token limit. Actual reservation (upsert) is done by incrementJobCount. */
  async checkQuota(userId: string, apiKeyMode: string): Promise<void> {
    const settings = await this.prisma.userAiSettings.findUnique({ where: { userId } });
    const maxJobs = settings?.maxDailyAiJobs ?? 20;
    const maxTokens = settings?.maxDailyTokenBudget ?? 100_000;

    const localDate = this.today();

    const usage = await this.prisma.userAiUsageDaily.findUnique({
      where: { userId_localDate_apiKeyMode: { userId, localDate, apiKeyMode } },
    });

    const jobCount = usage?.jobCount ?? 0;
    const totalTokens = usage?.totalTokens ?? 0;

    if (jobCount >= maxJobs) {
      throw new BadRequestException({
        errorCode: 'DAILY_JOB_LIMIT_EXCEEDED',
        message: `Daily AI job limit (${maxJobs}) exceeded. Current: ${jobCount}`,
      });
    }

    if (totalTokens >= maxTokens) {
      throw new BadRequestException({
        errorCode: 'DAILY_TOKEN_BUDGET_EXCEEDED',
        message: `Daily token budget (${maxTokens}) exceeded. Current: ${totalTokens}`,
      });
    }
  }

  /** Increment job count (called on job creation) */
  async incrementJobCount(userId: string, apiKeyMode: string): Promise<void> {
    const localDate = this.today();
    await this.prisma.userAiUsageDaily.upsert({
      where: { userId_localDate_apiKeyMode: { userId, localDate, apiKeyMode } },
      create: { userId, localDate, apiKeyMode, jobCount: 1 },
      update: { jobCount: { increment: 1 } },
    });
  }

  /** Record token consumption from invocation log (called after Runtime call) */
  async recordTokenUsage(
    userId: string,
    apiKeyMode: string,
    inputTokens: number,
    outputTokens: number,
    totalTokens: number,
    costEstimate?: number,
  ): Promise<void> {
    const localDate = this.today();
    await this.prisma.userAiUsageDaily.upsert({
      where: { userId_localDate_apiKeyMode: { userId, localDate, apiKeyMode } },
      create: {
        userId, localDate, apiKeyMode,
        inputTokens, outputTokens, totalTokens,
        costEstimate: costEstimate ?? 0,
        jobCount: 0,
      },
      update: {
        inputTokens: { increment: inputTokens },
        outputTokens: { increment: outputTokens },
        totalTokens: { increment: totalTokens },
        costEstimate: { increment: costEstimate ?? 0 },
      },
    });
  }

  /** Get today's usage summary for a user */
  async getUsage(userId: string) {
    const localDate = this.today();
    const [platform, userKey] = await Promise.all([
      this.prisma.userAiUsageDaily.findUnique({
        where: { userId_localDate_apiKeyMode: { userId, localDate, apiKeyMode: 'platform_key' } },
      }),
      this.prisma.userAiUsageDaily.findUnique({
        where: { userId_localDate_apiKeyMode: { userId, localDate, apiKeyMode: 'user_deepseek_key' } },
      }),
    ]);
    return {
      platformKey: platform ?? { jobCount: 0, totalTokens: 0 },
      userDeepseekKey: userKey ?? { jobCount: 0, totalTokens: 0 },
    };
  }
}
