import { prisma } from '@/lib/prisma'

export type Period = '7d' | '30d' | '90d' | 'mtd'

export interface CostKpi {
  totalCostUsd: number
  totalTokens: number
  jobCount: number
  avgPerDayUsd: number
  cacheSavingsUsd: number
  topModelId: string | null
  topModelCostUsd: number
}

export interface CostByDayRow {
  day: string
  costUsd: number
}

export interface CostByModelRow {
  modelId: string
  costUsd: number
  jobCount: number
}

export interface CostByKindRow {
  kind: string
  costUsd: number
  jobCount: number
}

export interface CacheEfficiency {
  cacheReadTokens: number
  uncachedInputTokens: number
  cacheHitRatio: number
  savingsUsd: number
  spentOnCacheWriteUsd: number
}

function periodToDays(period: Period, now: Date = new Date()): number {
  switch (period) {
    case '7d':
      return 7
    case '30d':
      return 30
    case '90d':
      return 90
    case 'mtd':
      return now.getUTCDate()
  }
}

function periodStart(period: Period, now: Date = new Date()): Date {
  if (period === 'mtd') {
    return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1))
  }
  const days = periodToDays(period, now)
  return new Date(now.getTime() - days * 86_400_000)
}

type RawKpiRow = {
  total_cost: number | null
  total_tokens: bigint
  job_count: bigint
  cache_savings: number | null
}

type RawTopModelRow = {
  model_id: string | null
  cost: number | null
}

export async function getCostKpi(userId: string, period: Period): Promise<CostKpi> {
  const start = periodStart(period)
  const days = Math.max(periodToDays(period), 1)

  const [kpiRows, topModelRows] = await Promise.all([
    prisma.$queryRaw<RawKpiRow[]>`
      SELECT
        SUM(
          cj.input_tokens  * mp.input_price_per_1m  / 1000000.0
          + cj.output_tokens * mp.output_price_per_1m / 1000000.0
          + cj.cache_read_tokens  * mp.cache_read_price_per_1m  / 1000000.0
          + cj.cache_write_tokens * mp.cache_write_price_per_1m / 1000000.0
          + COALESCE(cj.actual_thinking_tokens, 0) * mp.input_price_per_1m / 1000000.0
        ) FILTER (WHERE cj.input_tokens IS NOT NULL) AS total_cost,
        COALESCE(SUM(
          cj.input_tokens + cj.output_tokens + cj.cache_read_tokens + cj.cache_write_tokens
          + COALESCE(cj.actual_thinking_tokens, 0)
        ), 0) AS total_tokens,
        COUNT(*) FILTER (WHERE cj.input_tokens IS NOT NULL) AS job_count,
        SUM(
          cj.cache_read_tokens * (mp.input_price_per_1m - mp.cache_read_price_per_1m) / 1000000.0
        ) FILTER (WHERE cj.cache_read_tokens IS NOT NULL) AS cache_savings
      FROM claude_jobs cj
      LEFT JOIN model_prices mp ON mp.model_id = cj.model_id
      WHERE cj.user_id = ${userId}
        AND cj.status = 'DONE'
        AND cj.finished_at >= ${start}
    `,
    prisma.$queryRaw<RawTopModelRow[]>`
      SELECT
        cj.model_id,
        SUM(
          cj.input_tokens  * mp.input_price_per_1m  / 1000000.0
          + cj.output_tokens * mp.output_price_per_1m / 1000000.0
          + cj.cache_read_tokens  * mp.cache_read_price_per_1m  / 1000000.0
          + cj.cache_write_tokens * mp.cache_write_price_per_1m / 1000000.0
          + COALESCE(cj.actual_thinking_tokens, 0) * mp.input_price_per_1m / 1000000.0
        ) AS cost
      FROM claude_jobs cj
      LEFT JOIN model_prices mp ON mp.model_id = cj.model_id
      WHERE cj.user_id = ${userId}
        AND cj.status = 'DONE'
        AND cj.finished_at >= ${start}
        AND cj.input_tokens IS NOT NULL
        AND cj.model_id IS NOT NULL
      GROUP BY cj.model_id
      ORDER BY cost DESC NULLS LAST
      LIMIT 1
    `,
  ])

  const kpi = kpiRows[0]
  const totalCost = Number(kpi?.total_cost ?? 0)
  const top = topModelRows[0]

  return {
    totalCostUsd: totalCost,
    totalTokens: Number(kpi?.total_tokens ?? 0),
    jobCount: Number(kpi?.job_count ?? 0),
    avgPerDayUsd: totalCost / days,
    cacheSavingsUsd: Number(kpi?.cache_savings ?? 0),
    topModelId: top?.model_id ?? null,
    topModelCostUsd: Number(top?.cost ?? 0),
  }
}

type RawDayRow = {
  day: Date
  cost: number | null
}

export async function getCostByDay(userId: string, period: Period): Promise<CostByDayRow[]> {
  const start = periodStart(period)

  const rows = await prisma.$queryRaw<RawDayRow[]>`
    SELECT
      DATE(cj.finished_at) AS day,
      SUM(
        cj.input_tokens  * mp.input_price_per_1m  / 1000000.0
        + cj.output_tokens * mp.output_price_per_1m / 1000000.0
        + cj.cache_read_tokens  * mp.cache_read_price_per_1m  / 1000000.0
        + cj.cache_write_tokens * mp.cache_write_price_per_1m / 1000000.0
        + COALESCE(cj.actual_thinking_tokens, 0) * mp.input_price_per_1m / 1000000.0
      ) FILTER (WHERE cj.input_tokens IS NOT NULL) AS cost
    FROM claude_jobs cj
    LEFT JOIN model_prices mp ON mp.model_id = cj.model_id
    WHERE cj.user_id = ${userId}
      AND cj.status = 'DONE'
      AND cj.finished_at >= ${start}
    GROUP BY DATE(cj.finished_at)
    ORDER BY day ASC
  `

  return rows.map(r => ({
    day: r.day.toISOString().slice(0, 10),
    costUsd: Number(r.cost ?? 0),
  }))
}

type RawModelRow = {
  model_id: string
  cost: number | null
  job_count: bigint
}

export async function getCostByModel(userId: string, period: Period): Promise<CostByModelRow[]> {
  const start = periodStart(period)

  const rows = await prisma.$queryRaw<RawModelRow[]>`
    SELECT
      cj.model_id,
      SUM(
        cj.input_tokens  * mp.input_price_per_1m  / 1000000.0
        + cj.output_tokens * mp.output_price_per_1m / 1000000.0
        + cj.cache_read_tokens  * mp.cache_read_price_per_1m  / 1000000.0
        + cj.cache_write_tokens * mp.cache_write_price_per_1m / 1000000.0
        + COALESCE(cj.actual_thinking_tokens, 0) * mp.input_price_per_1m / 1000000.0
      ) AS cost,
      COUNT(*) AS job_count
    FROM claude_jobs cj
    LEFT JOIN model_prices mp ON mp.model_id = cj.model_id
    WHERE cj.user_id = ${userId}
      AND cj.status = 'DONE'
      AND cj.finished_at >= ${start}
      AND cj.input_tokens IS NOT NULL
      AND cj.model_id IS NOT NULL
    GROUP BY cj.model_id
    ORDER BY cost DESC NULLS LAST
    LIMIT 5
  `

  return rows.map(r => ({
    modelId: r.model_id,
    costUsd: Number(r.cost ?? 0),
    jobCount: Number(r.job_count),
  }))
}

type RawKindRow = {
  kind: string
  cost: number | null
  job_count: bigint
}

export async function getCostByKind(userId: string, period: Period): Promise<CostByKindRow[]> {
  const start = periodStart(period)

  const rows = await prisma.$queryRaw<RawKindRow[]>`
    SELECT
      cj.kind::text AS kind,
      SUM(
        cj.input_tokens  * mp.input_price_per_1m  / 1000000.0
        + cj.output_tokens * mp.output_price_per_1m / 1000000.0
        + cj.cache_read_tokens  * mp.cache_read_price_per_1m  / 1000000.0
        + cj.cache_write_tokens * mp.cache_write_price_per_1m / 1000000.0
        + COALESCE(cj.actual_thinking_tokens, 0) * mp.input_price_per_1m / 1000000.0
      ) AS cost,
      COUNT(*) AS job_count
    FROM claude_jobs cj
    LEFT JOIN model_prices mp ON mp.model_id = cj.model_id
    WHERE cj.user_id = ${userId}
      AND cj.status = 'DONE'
      AND cj.finished_at >= ${start}
      AND cj.input_tokens IS NOT NULL
    GROUP BY cj.kind
    ORDER BY cost DESC NULLS LAST
    LIMIT 5
  `

  return rows.map(r => ({
    kind: r.kind,
    costUsd: Number(r.cost ?? 0),
    jobCount: Number(r.job_count),
  }))
}

type RawCacheRow = {
  cache_read_tokens: bigint
  uncached_input_tokens: bigint
  savings: number | null
  cache_write_cost: number | null
}

export async function getCacheEfficiency(
  userId: string,
  period: Period,
): Promise<CacheEfficiency> {
  const start = periodStart(period)

  const rows = await prisma.$queryRaw<RawCacheRow[]>`
    SELECT
      COALESCE(SUM(cj.cache_read_tokens), 0) AS cache_read_tokens,
      COALESCE(SUM(cj.input_tokens), 0) AS uncached_input_tokens,
      SUM(
        cj.cache_read_tokens * (mp.input_price_per_1m - mp.cache_read_price_per_1m) / 1000000.0
      ) FILTER (WHERE cj.cache_read_tokens IS NOT NULL) AS savings,
      SUM(
        cj.cache_write_tokens * mp.cache_write_price_per_1m / 1000000.0
      ) FILTER (WHERE cj.cache_write_tokens IS NOT NULL) AS cache_write_cost
    FROM claude_jobs cj
    LEFT JOIN model_prices mp ON mp.model_id = cj.model_id
    WHERE cj.user_id = ${userId}
      AND cj.status = 'DONE'
      AND cj.finished_at >= ${start}
  `

  const r = rows[0]
  const cacheReadTokens = Number(r?.cache_read_tokens ?? 0)
  const uncachedInputTokens = Number(r?.uncached_input_tokens ?? 0)
  const totalInputLike = cacheReadTokens + uncachedInputTokens

  return {
    cacheReadTokens,
    uncachedInputTokens,
    cacheHitRatio: totalInputLike > 0 ? cacheReadTokens / totalInputLike : 0,
    savingsUsd: Number(r?.savings ?? 0),
    spentOnCacheWriteUsd: Number(r?.cache_write_cost ?? 0),
  }
}
