/**
 * Platform Cost Configuration
 * ──────────────────────────
 * Single source of truth for vendor pricing + service metadata used
 * by /api/admin/cost-center and the Cost Center page. These numbers
 * are INTERNAL reference prices we track for operational forecasting
 * — the authoritative billing lives on each vendor's dashboard.
 *
 * Update this file when vendor pricing changes. Every consumer reads
 * from PLATFORM_SERVICES rather than hardcoding rates, so a single
 * edit reconciles all rollups + projections.
 *
 * Last verified: April 2026. Verify against vendor billing dashboards
 * before publishing investor-facing cost numbers.
 */

// ─── Service catalogue ──────────────────────────────────────────
//
// Each entry carries the public name + vendor + dashboard URL +
// billing category + its pricing table (shape varies per service).
// Every service on the platform is USAGE-billed — there are no
// flat-fee vendors — so `billing` is always "usage". Kept as a
// field for future flexibility (if we add a flat-fee service we
// can flag it here without restructuring).

export interface ServiceCommon {
  name: string;
  vendor: string;
  url: string;
  category: "ai" | "database" | "hosting" | "auth" | "serp" | "email" | "cdn";
  billing: "usage";
  /** Caveat surfaced on the cost card — explains what "estimated"
   *  means for this vendor. */
  note: string;
}

export interface AnthropicService   extends ServiceCommon { category: "ai";       pricing: Record<string, { inputPer1M: number; outputPer1M: number; cachedInputPer1M?: number }> }
export interface OpenAIService      extends ServiceCommon { category: "ai";       pricing: Record<string, { inputPer1M: number; outputPer1M: number }> }
export interface NeonService        extends ServiceCommon { category: "database"; pricing: { computePerHour: number; storagePerGBMonth: number; writtenDataPerGB: number; tier: string; baseMonthly: number } }
export interface VercelService      extends ServiceCommon { category: "hosting";  pricing: { proBase: number; functionInvocationsIncluded: number; functionOveragePer100K: number; bandwidthIncludedGB: number; bandwidthOveragePerGB: number } }
export interface ClerkService       extends ServiceCommon { category: "auth";     pricing: { freeMAU: number; businessBase: number; overagePerMAU: number } }
export interface DataForSeoService  extends ServiceCommon { category: "serp";     pricing: { serpRegular: number; serpLive: number; keywordsForSite: number; backlinks: number; rankTracker: number } }
export interface ResendService      extends ServiceCommon { category: "email";    pricing: { freeEmails: number; overagePerEmail: number } }

export const PLATFORM_SERVICES = {
  anthropic: {
    name: "Anthropic Claude",
    vendor: "Anthropic",
    billing: "usage",
    url: "https://console.anthropic.com",
    category: "ai",
    pricing: {
      "claude-sonnet-4-6":          { inputPer1M: 3,   outputPer1M: 15,  cachedInputPer1M: 0.30 },
      "claude-sonnet-4-20250514":   { inputPer1M: 3,   outputPer1M: 15,  cachedInputPer1M: 0.30 },
      "claude-sonnet-4-5":          { inputPer1M: 3,   outputPer1M: 15,  cachedInputPer1M: 0.30 },
      "claude-opus-4-6":            { inputPer1M: 15,  outputPer1M: 75,  cachedInputPer1M: 1.50 },
      "claude-haiku-4-5-20251001":  { inputPer1M: 1,   outputPer1M: 5,   cachedInputPer1M: 0.10 },
      "claude-haiku-4-5":           { inputPer1M: 1,   outputPer1M: 5,   cachedInputPer1M: 0.10 },
      "claude-3-5-sonnet-20241022": { inputPer1M: 3,   outputPer1M: 15 },
      "claude-3-haiku-20240307":    { inputPer1M: 0.25, outputPer1M: 1.25 },
    },
    note: "Estimated from AiUsageLog — tokens + model × per-MTok pricing. Verify against Anthropic billing dashboard.",
  } satisfies AnthropicService,

  openai: {
    name: "OpenAI",
    vendor: "OpenAI",
    billing: "usage",
    url: "https://platform.openai.com/usage",
    category: "ai",
    pricing: {
      "gpt-4o":        { inputPer1M: 2.5,  outputPer1M: 10 },
      "gpt-4o-mini":   { inputPer1M: 0.15, outputPer1M: 0.60 },
    },
    note: "Estimated from AiUsageLog where model starts with gpt-*. Verify against OpenAI usage dashboard.",
  } satisfies OpenAIService,

  neon: {
    name: "Neon PostgreSQL",
    vendor: "Neon",
    billing: "usage",
    url: "https://console.neon.tech",
    category: "database",
    pricing: {
      baseMonthly: 19,            // Launch tier
      tier: "launch",
      computePerHour: 0.16,
      storagePerGBMonth: 0.75,
      writtenDataPerGB: 0.096,
    },
    note: "Storage estimated from row counts (avg 500B/row); pg_database_size isn't reliable over Neon's pooler. Verify against Neon billing dashboard.",
  } satisfies NeonService,

  vercel: {
    name: "Vercel Hosting",
    vendor: "Vercel",
    billing: "usage",
    url: "https://vercel.com/dashboard",
    category: "hosting",
    pricing: {
      proBase: 20,
      functionInvocationsIncluded: 1_000_000,
      functionOveragePer100K: 0.60,
      bandwidthIncludedGB: 1000,
      bandwidthOveragePerGB: 0.15,
    },
    note: "Pro plan $20 base + usage overages. Function count estimated from TrackedEvent + SiteEvent rows × 4 (collect + non-ingest route ratio).",
  } satisfies VercelService,

  clerk: {
    name: "Clerk Auth",
    vendor: "Clerk",
    billing: "usage",
    url: "https://dashboard.clerk.com",
    category: "auth",
    pricing: {
      freeMAU: 10_000,
      businessBase: 25,
      overagePerMAU: 0.02,
    },
    note: "Free up to 10K MAU, then $25 base + $0.02/MAU. MAU estimated from distinct User activity in the window.",
  } satisfies ClerkService,

  dataforseo: {
    name: "DataForSEO",
    vendor: "DataForSEO",
    billing: "usage",
    url: "https://app.dataforseo.com",
    category: "serp",
    pricing: {
      serpRegular:     0.002,
      serpLive:        0.004,
      keywordsForSite: 0.05,
      backlinks:       0.002,
      rankTracker:     0.01,
    },
    note: "Estimated from PlatformEvent action counts (serp_query / keyword_research / rank_tracker_scan / backlink_sync). Verify against DataForSEO billing.",
  } satisfies DataForSeoService,

  resend: {
    name: "Resend Email",
    vendor: "Resend",
    billing: "usage",
    url: "https://resend.com/overview",
    category: "email",
    pricing: {
      freeEmails:      3_000,
      overagePerEmail: 0.001,
    },
    note: "Free up to 3K emails/month, then $0.001 per email. Counted from PlatformEvent where action = 'email_sent'.",
  } satisfies ResendService,
};

export type ServiceKey = keyof typeof PLATFORM_SERVICES;

// ─── Per-service cost helpers ───────────────────────────────────

/** Cost of an Anthropic / OpenAI (model, inputTokens, outputTokens)
 *  triple. Unknown models fall back to Sonnet pricing so the
 *  estimate is conservative rather than zero. */
export function costFor(model: string, inputTokens: number, outputTokens: number): number {
  const key = (model || "").toLowerCase();
  // Cast through Record<string, …> because the literal-keyed pricing
  // tables are typed as a discriminated object and Typescript otherwise
  // forbids dynamic-string indexing.
  const anth = (PLATFORM_SERVICES.anthropic.pricing as Record<string, { inputPer1M: number; outputPer1M: number }>)[key];
  const oai  = (PLATFORM_SERVICES.openai.pricing as Record<string, { inputPer1M: number; outputPer1M: number }>)[key];
  const price = anth ?? oai ?? PLATFORM_SERVICES.anthropic.pricing["claude-sonnet-4-6"];
  return (inputTokens * price.inputPer1M + outputTokens * price.outputPer1M) / 1_000_000;
}

/** Identify whether a model string belongs to Anthropic or OpenAI.
 *  Unknown models default to anthropic (our primary vendor). */
export function modelVendor(model: string): "anthropic" | "openai" {
  const m = (model || "").toLowerCase();
  const oaiKeys = Object.keys(PLATFORM_SERVICES.openai.pricing);
  if (oaiKeys.includes(m)) return "openai";
  if (m.startsWith("gpt-") || m.startsWith("o1-") || m.startsWith("o3-")) return "openai";
  return "anthropic";
}

/** Estimate monthly Neon cost from storage (GB) + compute hours.
 *  storageGb is typically inferred from row counts since
 *  pg_database_size isn't reliable across Neon's pooler. */
export function estimateNeonCost(storageGb: number, computeHours: number): number {
  const p = PLATFORM_SERVICES.neon.pricing;
  return p.baseMonthly + storageGb * p.storagePerGBMonth + computeHours * p.computePerHour;
}

/** Estimate Clerk cost given MAU. Free below 10K. */
export function estimateClerkCost(mau: number): number {
  const p = PLATFORM_SERVICES.clerk.pricing;
  if (mau <= p.freeMAU) return 0;
  return p.businessBase + (mau - p.freeMAU) * p.overagePerMAU;
}

/** DataForSEO cost given per-endpoint call counts. */
export function estimateDataForSeoCost(calls: {
  serp?: number; keywords?: number; backlinks?: number; rankTracker?: number;
}): number {
  const p = PLATFORM_SERVICES.dataforseo.pricing;
  return (calls.serp ?? 0) * p.serpRegular
    + (calls.keywords ?? 0) * p.keywordsForSite
    + (calls.backlinks ?? 0) * p.backlinks
    + (calls.rankTracker ?? 0) * p.rankTracker;
}

/** Vercel cost given function invocations + bandwidth (GB). */
export function estimateVercelCost(invocations: number, bandwidthGb: number): number {
  const p = PLATFORM_SERVICES.vercel.pricing;
  const invOver = Math.max(0, invocations - p.functionInvocationsIncluded);
  const bwOver = Math.max(0, bandwidthGb - p.bandwidthIncludedGB);
  return p.proBase
    + (invOver / 100_000) * p.functionOveragePer100K
    + bwOver * p.bandwidthOveragePerGB;
}

/** Resend cost given email send count. */
export function estimateEmailCost(sends: number): number {
  const p = PLATFORM_SERVICES.resend.pricing;
  if (sends <= p.freeEmails) return 0;
  return (sends - p.freeEmails) * p.overagePerEmail;
}

// ─── Alert thresholds ──────────────────────────────────────────

export const COST_ALERT_THRESHOLDS = {
  totalMonthlyUsd: Number(process.env.COST_ALERT_TOTAL ?? 200),
  aiMonthlyUsd:    Number(process.env.COST_ALERT_AI ?? 50),
  dbMonthlyUsd:    Number(process.env.COST_ALERT_DB ?? 50),
  serpMonthlyUsd:  Number(process.env.COST_ALERT_SERP ?? 30),
};

// ─── Display labels (backwards-compat shim) ────────────────────
//
// Pre-PLATFORM_SERVICES consumers read SERVICE_LABELS with the
// old service keys (ai / database / auth / serp / hosting / email
// / siteTag). Derive from PLATFORM_SERVICES so a pricing-table
// edit keeps the UI's vendor names in sync.

export const SERVICE_LABELS: Record<string, { label: string; vendor: string; vendorUrl: string }> = {
  ai:        { label: "AI / LLM",        vendor: PLATFORM_SERVICES.anthropic.vendor,  vendorUrl: PLATFORM_SERVICES.anthropic.url },
  anthropic: { label: PLATFORM_SERVICES.anthropic.name,  vendor: PLATFORM_SERVICES.anthropic.vendor,  vendorUrl: PLATFORM_SERVICES.anthropic.url },
  openai:    { label: PLATFORM_SERVICES.openai.name,     vendor: PLATFORM_SERVICES.openai.vendor,     vendorUrl: PLATFORM_SERVICES.openai.url },
  database:  { label: PLATFORM_SERVICES.neon.name,       vendor: PLATFORM_SERVICES.neon.vendor,       vendorUrl: PLATFORM_SERVICES.neon.url },
  neon:      { label: PLATFORM_SERVICES.neon.name,       vendor: PLATFORM_SERVICES.neon.vendor,       vendorUrl: PLATFORM_SERVICES.neon.url },
  auth:      { label: PLATFORM_SERVICES.clerk.name,      vendor: PLATFORM_SERVICES.clerk.vendor,      vendorUrl: PLATFORM_SERVICES.clerk.url },
  clerk:     { label: PLATFORM_SERVICES.clerk.name,      vendor: PLATFORM_SERVICES.clerk.vendor,      vendorUrl: PLATFORM_SERVICES.clerk.url },
  serp:      { label: PLATFORM_SERVICES.dataforseo.name, vendor: PLATFORM_SERVICES.dataforseo.vendor, vendorUrl: PLATFORM_SERVICES.dataforseo.url },
  dataforseo:{ label: PLATFORM_SERVICES.dataforseo.name, vendor: PLATFORM_SERVICES.dataforseo.vendor, vendorUrl: PLATFORM_SERVICES.dataforseo.url },
  hosting:   { label: PLATFORM_SERVICES.vercel.name,     vendor: PLATFORM_SERVICES.vercel.vendor,     vendorUrl: PLATFORM_SERVICES.vercel.url },
  vercel:    { label: PLATFORM_SERVICES.vercel.name,     vendor: PLATFORM_SERVICES.vercel.vendor,     vendorUrl: PLATFORM_SERVICES.vercel.url },
  email:     { label: PLATFORM_SERVICES.resend.name,     vendor: PLATFORM_SERVICES.resend.vendor,     vendorUrl: PLATFORM_SERVICES.resend.url },
  resend:    { label: PLATFORM_SERVICES.resend.name,     vendor: PLATFORM_SERVICES.resend.vendor,     vendorUrl: PLATFORM_SERVICES.resend.url },
  siteTag:   { label: "Site Tag / CDN",  vendor: PLATFORM_SERVICES.vercel.vendor,     vendorUrl: PLATFORM_SERVICES.vercel.url },
};

// Legacy exports kept so existing imports (ANTHROPIC_PRICING /
// NEON_TIERS / CLERK_PRICING / etc.) keep compiling. New code
// should import PLATFORM_SERVICES + the helpers above.
export const ANTHROPIC_PRICING = Object.fromEntries(
  Object.entries(PLATFORM_SERVICES.anthropic.pricing).map(([k, v]) => {
    // cachedInputPer1M is optional on legacy entries (the two
    // claude-3.x models don't carry it). Read defensively.
    const cached = (v as { cachedInputPer1M?: number }).cachedInputPer1M;
    return [k, { inputPerMtok: v.inputPer1M, outputPerMtok: v.outputPer1M, cachedInputPerMtok: cached }];
  }),
);
export const OPENAI_PRICING = Object.fromEntries(
  Object.entries(PLATFORM_SERVICES.openai.pricing).map(([k, v]) => [
    k, { inputPerMtok: v.inputPer1M, outputPerMtok: v.outputPer1M },
  ]),
);
