import { gte, sql } from "drizzle-orm";
import type {
  OpsAppBlock,
  OpsActivationBlock,
  OpsAiScanBlock,
  OpsEngagementBlock,
  OpsPaymentBlock,
  OpsQueueBlock,
  OpsSafetyBlock,
  OpsSummary,
  OpsEconomyBlock,
  OpsUsersBlock,
  OpsSubscriptionsBlock,
  OpsStoreBlock,
  OpsFeedbackBlock,
  OpsWallBlock,
  OpsWallTile,
  OpsAlarm,
  OpsAlarmBlock,
  OpsGeminiBlock,
} from "@app/shared-types";
import { SUBSCRIPTION_PLANS, type SubscriptionPlan } from "@app/shared-types";
import type { Database } from "./client.js";
import { schema } from "./index.js";
import { geminiUsage } from "./schema/index.js";

const BUDGET_KEY_PREFIX = "gemini:daily:budget:";

export interface OpsQueueSummary {
  vantande: number;
  aktiva: number;
  misslyckade_24h: number;
  aldsta_vantande_sek: number | null;
  workers_ok: boolean;
}

export interface ComputeOpsSummaryOptions {
  db: Database;
  /** Gemini daily budget in USD (0 = disabled). */
  budgetUsd: number;
  /** Current daily spend in USD, from Redis budget store. */
  dailySpendUsd?: number | null;
  queueSummary?: OpsQueueSummary;
  /** Optional override for subscription plan prices in SEK öre per month. */
  planPrices?: Partial<Record<SubscriptionPlan, number | null>>;
}

const MICROCENTS_PER_USD = 100_000_000;
const ESTIMATED_SCAN_COST_USD = 0.0015;
const ESTIMATED_SCAN_COST_MICROCENTS = Math.round(ESTIMATED_SCAN_COST_USD * MICROCENTS_PER_USD);

function asNumber(value: unknown): number {
  return Number(value ?? 0);
}

function formatSekKr(oer: number | null): string {
  if (oer === null || Number.isNaN(oer)) return "—";
  const kr = oer / 100;
  // Mellanslag (0x20) som tusentalsavgränsare, utan ören.
  const formatted = new Intl.NumberFormat("sv-SE", { maximumFractionDigits: 0 }).format(kr);
  return `${formatted.replace(/\s/g, " ")} kr`;
}

function formatPercent(ratio: number | null): string {
  if (ratio === null || Number.isNaN(ratio)) return "—";
  return `${Math.round(ratio * 100)} %`;
}

function formatInt(n: number | null): string {
  if (n === null || Number.isNaN(n)) return "—";
  return String(n);
}

function safeDiv(numerator: number, denominator: number): number | null {
  if (denominator === 0) return null;
  return numerator / denominator;
}

function roundRate(value: number | null): number | null {
  if (value === null || Number.isNaN(value)) return null;
  return Math.round(value * 1_000_000) / 1_000_000;
}

async function countEvents(db: Database, name: string, hours: number): Promise<number> {
  const result = await db.execute(sql`
    SELECT count(*)::int AS n
    FROM product_analytics_events
    WHERE event_name = ${name}
      AND occurred_at >= now() - make_interval(hours => ${hours})`);
  const row = (Array.isArray(result) ? result[0] : result.rows[0]) as { n: number } | undefined;
  return asNumber(row?.n);
}

async function aiScanBlock(
  db: Database,
  budgetUsd: number,
  dailySpendUsd: number | null | undefined,
): Promise<OpsAiScanBlock> {
  const scans24h = await countEvents(db, "scan_completed", 24);
  const scans7d = await countEvents(db, "scan_completed", 24 * 7);
  const started24h = await countEvents(db, "scan_started", 24);
  const failed24h = await countEvents(db, "scan_failed", 24);

  const latency = await db.execute(sql`
    SELECT
      percentile_cont(0.5) WITHIN GROUP (ORDER BY (properties->>'latencyMs')::int) AS p50,
      percentile_cont(0.95) WITHIN GROUP (ORDER BY (properties->>'latencyMs')::int) AS p95
    FROM product_analytics_events
    WHERE event_name = 'scan_completed'
      AND occurred_at >= now() - interval '24 hours'
      AND properties->>'latencyMs' IS NOT NULL
  `);
  const latencyRow = (Array.isArray(latency) ? latency[0] : latency.rows[0]) as
    { p50: string | number | null; p95: string | number | null } | undefined;

  const latestErrors = await db.execute(sql`
    SELECT properties->>'errorCode' AS code, occurred_at AS tid
    FROM product_analytics_events
    WHERE event_name = 'scan_failed'
      AND occurred_at >= now() - interval '24 hours'
    ORDER BY occurred_at DESC
    LIMIT 5
  `);
  const latestErrorsRows = (
    Array.isArray(latestErrors) ? latestErrors : latestErrors.rows
  ) as Array<{
    code: string | null;
    tid: string | Date;
  }>;

  const month = await db.execute(sql`
    SELECT COALESCE(sum(ai_cost_usd_microcents), 0)::bigint AS total
    FROM ai_usage_counters
    WHERE month = to_char(now() AT TIME ZONE 'UTC', 'YYYY-MM')
  `);
  const monthRow = (Array.isArray(month) ? month[0] : month.rows[0]) as
    { total: bigint | number } | undefined;
  const monthlyCostMicrocents =
    typeof monthRow?.total === "bigint" ? Number(monthRow.total) : asNumber(monthRow?.total);

  const exact24h = await db.execute(sql`
    SELECT COALESCE(sum(cost_usd), 0)::float AS total
    FROM scan_jobs
    WHERE cost_usd IS NOT NULL
      AND updated_at >= now() - interval '24 hours'
      AND status IN ('awaiting_confirmation', 'completed')
  `);
  const exact24hRow = (Array.isArray(exact24h) ? exact24h[0] : exact24h.rows[0]) as
    { total: number } | undefined;
  const exactCostUsd = asNumber(exact24hRow?.total);

  let cost24hMicrocents: number | null = null;
  let cost24hApprox = false;
  if (exactCostUsd > 0) {
    cost24hMicrocents = Math.round(exactCostUsd * MICROCENTS_PER_USD);
  } else if (scans24h > 0) {
    cost24hMicrocents = scans24h * ESTIMATED_SCAN_COST_MICROCENTS;
    cost24hApprox = true;
  }

  let budgetAndel: number | null = null;
  if (budgetUsd > 0 && dailySpendUsd != null && !Number.isNaN(dailySpendUsd)) {
    budgetAndel = Math.min(1, dailySpendUsd / budgetUsd);
  }

  return {
    scans_24h: scans24h,
    scans_7d: scans7d,
    lyckandegrad_24h: roundRate(safeDiv(scans24h, started24h)),
    latens_p50_ms: latencyRow?.p50 != null ? Math.round(Number(latencyRow.p50)) : null,
    latens_p95_ms: latencyRow?.p95 != null ? Math.round(Number(latencyRow.p95)) : null,
    felfrekvens_24h: roundRate(safeDiv(failed24h, started24h)),
    senaste_fel:
      latestErrorsRows.length > 0
        ? latestErrorsRows.map((r) => ({
            kod: r.code,
            tid: r.tid instanceof Date ? r.tid.toISOString() : String(r.tid),
          }))
        : null,
    kostnad_usd_mikrocent_manad: monthlyCostMicrocents,
    kostnad_usd_mikrocent_24h: cost24hMicrocents,
    kostnad_usd_mikrocent_24h_uppskattad: cost24hApprox,
    budget_andel: roundRate(budgetAndel),
    as_of: new Date().toISOString(),
  };
}

async function conversionWithin(
  db: Database,
  fromEvent: string,
  toEvent: string,
  cohortDays: number,
  withinHours: number,
): Promise<number | null> {
  const result = await db.execute(sql`
    WITH cohort AS (
      SELECT DISTINCT user_id, occurred_at AS created_at
      FROM product_analytics_events
      WHERE event_name = ${fromEvent}
        AND occurred_at >= now() - make_interval(days => ${cohortDays})
        AND user_id IS NOT NULL
    ),
    converted AS (
      SELECT DISTINCT c.user_id
      FROM cohort c
      INNER JOIN product_analytics_events e ON e.user_id = c.user_id
      WHERE e.event_name = ${toEvent}
        AND e.occurred_at >= c.created_at
        AND e.occurred_at < c.created_at + make_interval(hours => ${withinHours})
    )
    SELECT
      (SELECT count(*) FROM cohort) AS total,
      (SELECT count(*) FROM converted) AS converted
  `);
  const row = (Array.isArray(result) ? result[0] : result.rows[0]) as
    { total: number; converted: number } | undefined;
  if (!row || asNumber(row.total) === 0) return null;
  return asNumber(row.converted) / asNumber(row.total);
}

async function householdConversionWithin(
  db: Database,
  fromEvent: string,
  toEvent: string,
  cohortDays: number,
): Promise<number | null> {
  const result = await db.execute(sql`
    WITH cohort AS (
      SELECT DISTINCT household_id, occurred_at AS created_at
      FROM product_analytics_events
      WHERE event_name = ${fromEvent}
        AND occurred_at >= now() - make_interval(days => ${cohortDays})
        AND household_id IS NOT NULL
    ),
    converted AS (
      SELECT DISTINCT c.household_id
      FROM cohort c
      INNER JOIN product_analytics_events e ON e.household_id = c.household_id
      WHERE e.event_name = ${toEvent}
        AND e.occurred_at >= c.created_at
        AND e.occurred_at < c.created_at + make_interval(days => ${cohortDays})
    )
    SELECT
      (SELECT count(*) FROM cohort) AS total,
      (SELECT count(*) FROM converted) AS converted
  `);
  const row = (Array.isArray(result) ? result[0] : result.rows[0]) as
    { total: number; converted: number } | undefined;
  if (!row || asNumber(row.total) === 0) return null;
  return asNumber(row.converted) / asNumber(row.total);
}

async function cohortRetention(db: Database, day: number): Promise<number | null> {
  const result = await db.execute(sql`
    WITH cohort AS (
      SELECT DISTINCT user_id,
             (occurred_at AT TIME ZONE 'UTC')::date AS cohort_date
      FROM product_analytics_events
      WHERE event_name = 'account_created'
        AND occurred_at >= now() - interval '30 days'
        AND user_id IS NOT NULL
    ),
    active AS (
      SELECT DISTINCT c.user_id
      FROM cohort c
      INNER JOIN product_analytics_events e ON e.user_id = c.user_id
      WHERE (e.occurred_at AT TIME ZONE 'UTC')::date = c.cohort_date + ${day}::int
    )
    SELECT
      (SELECT count(DISTINCT user_id) FROM cohort) AS total,
      (SELECT count(*) FROM active) AS active
  `);
  const row = (Array.isArray(result) ? result[0] : result.rows[0]) as
    { total: number; active: number } | undefined;
  if (!row || asNumber(row.total) === 0) return null;
  return asNumber(row.active) / asNumber(row.total);
}

async function activationBlock(db: Database): Promise<OpsActivationBlock> {
  const [scan24h, cook7d, householdSecond, d1, d7, d30] = await Promise.all([
    conversionWithin(db, "account_created", "scan_completed", 7, 24),
    conversionWithin(db, "account_created", "cooking_session_completed", 30, 24 * 7),
    householdConversionWithin(db, "household_created", "second_member_first_action", 30),
    cohortRetention(db, 1),
    cohortRetention(db, 7),
    cohortRetention(db, 30),
  ]);

  return {
    scan_inom_24h_andel: roundRate(scan24h),
    lagad_maltid_inom_7d_andel: roundRate(cook7d),
    hushall_andra_medlem_aktiv_andel: roundRate(householdSecond),
    retention_d1: roundRate(d1),
    retention_d7: roundRate(d7),
    retention_d30: roundRate(d30),
  };
}

async function engagementBlock(db: Database): Promise<OpsEngagementBlock> {
  const result = await db.execute(sql`
    SELECT
      count(*) FILTER (WHERE event_name = 'cooking_session_completed' AND occurred_at >= now() - interval '24 hours')::int AS c24,
      count(*) FILTER (WHERE event_name = 'cooking_session_completed' AND occurred_at >= now() - interval '7 days')::int AS c7,
      count(*) FILTER (WHERE event_name = 'recommendation_opened' AND occurred_at >= now() - interval '7 days')::int AS opened,
      count(*) FILTER (WHERE event_name = 'recommendations_viewed' AND occurred_at >= now() - interval '7 days')::int AS viewed
    FROM product_analytics_events
  `);
  const row = (Array.isArray(result) ? result[0] : result.rows[0]) as
    { c24: number; c7: number; opened: number; viewed: number } | undefined;

  const tips = await db.execute(sql`
    SELECT count(*)::int AS n
    FROM notifications
    WHERE type = 'proactive_tip'
      AND created_at >= now() - interval '24 hours'
  `);
  const tipsRow = (Array.isArray(tips) ? tips[0] : tips.rows[0]) as { n: number } | undefined;

  return {
    lagade_maltider_24h: asNumber(row?.c24),
    lagade_maltider_7d: asNumber(row?.c7),
    rek_ctr_7d: roundRate(safeDiv(asNumber(row?.opened), asNumber(row?.viewed))),
    puffar_skickade_24h: asNumber(tipsRow?.n),
    puffar_atgardsgrad_7d: null,
  };
}

async function paymentBlock(db: Database): Promise<OpsPaymentBlock> {
  const result = await db.execute(sql`
    SELECT
      count(*) FILTER (WHERE status = 'expired' OR (status != 'active' AND grace_period_expires_at < now()))::int AS failed,
      count(*) FILTER (WHERE status = 'in_grace' OR (grace_period_expires_at IS NOT NULL AND grace_period_expires_at >= now() AND status != 'active'))::int AS grace
    FROM subscriptions
  `);
  const row = (Array.isArray(result) ? result[0] : result.rows[0]) as
    { failed: number; grace: number } | undefined;

  const trials = await db.execute(sql`
    SELECT count(*)::int AS n
    FROM trials
    WHERE ends_at >= now()
      AND ends_at <= now() + interval '48 hours'
  `);
  const trialsRow = (Array.isArray(trials) ? trials[0] : trials.rows[0]) as
    { n: number } | undefined;

  const store = await db.execute(sql`
    SELECT
      count(*) FILTER (WHERE lower(notification_type) LIKE '%refund%')::int AS refunds,
      count(*) FILTER (WHERE lower(notification_type) LIKE '%chargeback%' OR lower(notification_type) LIKE '%revoke%')::int AS chargebacks
    FROM store_notifications
    WHERE created_at >= now() - interval '7 days'
  `);
  const storeRow = (Array.isArray(store) ? store[0] : store.rows[0]) as
    { refunds: number; chargebacks: number } | undefined;

  return {
    failed_nu: asNumber(row?.failed),
    grace_period_nu: asNumber(row?.grace),
    trials_utgar_48h: asNumber(trialsRow?.n),
    aterbetalningar_7d: storeRow ? asNumber(storeRow.refunds) : null,
    chargebacks_7d: storeRow ? asNumber(storeRow.chargebacks) : null,
  };
}

function defaultQueueBlock(): OpsQueueBlock {
  return {
    vantande: 0,
    aktiva: 0,
    misslyckade_24h: 0,
    aldsta_vantande_sek: null,
    workers_ok: false,
  };
}

async function safetyBlock(db: Database): Promise<OpsSafetyBlock> {
  const result = await db
    .select()
    .from(schema.opsSafetyCanary)
    .orderBy(sql`${schema.opsSafetyCanary.occurredAt} DESC`)
    .limit(1);
  const row = result[0];
  if (!row) {
    return {
      allergen_invariant_brott: 0,
      overifierade_visade: 0,
      food_safety_lint_avvisade_7d: null,
      senaste_kontroll: null,
    };
  }
  return {
    allergen_invariant_brott: row.allergenInvariantBrott,
    overifierade_visade: row.overifieradeVisade,
    food_safety_lint_avvisade_7d: row.foodSafetyLintAvvisade7d,
    senaste_kontroll: row.computedAt.toISOString(),
  };
}

function appBlock(): OpsAppBlock {
  return { app: "cibello", generated_at: new Date().toISOString() };
}

function getPlanPrices(): Record<SubscriptionPlan, number | null> {
  const defaults = Object.fromEntries(SUBSCRIPTION_PLANS.map((p) => [p, null])) as Record<
    SubscriptionPlan,
    number | null
  >;
  const envJson = process.env.SUBSCRIPTION_PLAN_PRICES_JSON;
  if (envJson) {
    try {
      const parsed = JSON.parse(envJson) as Record<string, number | null>;
      for (const plan of SUBSCRIPTION_PLANS) {
        if (parsed[plan] !== undefined) defaults[plan] = parsed[plan];
      }
    } catch {
      // Ignorera trasig env-JSON; använd null för alla planer.
    }
  }
  return defaults;
}

async function economyBlock(
  db: Database,
  planPrices: Record<SubscriptionPlan, number | null>,
): Promise<OpsEconomyBlock> {
  const active = await db.execute(sql`
    SELECT plan, count(*)::int AS n
    FROM subscriptions
    WHERE status = 'active'
      AND (expires_at IS NULL OR expires_at > now())
    GROUP BY plan
  `);
  const activeRows = (Array.isArray(active) ? active : active.rows) as Array<{
    plan: SubscriptionPlan;
    n: number;
  }>;
  let mrr: number | null = 0;
  for (const row of activeRows) {
    const price = planPrices[row.plan];
    if (price === null) {
      mrr = null;
      break;
    }
    mrr = (mrr ?? 0) + price * row.n;
  }

  async function revenueFor(hours: number): Promise<number | null> {
    const events = await db.execute(sql`
      SELECT properties
      FROM product_analytics_events
      WHERE event_name = 'subscription_started'
        AND occurred_at >= now() - make_interval(hours => ${hours})
    `);
    const rows = (Array.isArray(events) ? events : events.rows) as Array<{
      properties: { priceOer?: number; plan?: SubscriptionPlan } | null;
    }>;
    let total: number | null = 0;
    for (const row of rows) {
      const props = row.properties ?? {};
      const price =
        typeof props.priceOer === "number" ? props.priceOer : planPrices[props.plan ?? "free"];
      if (price === null) {
        total = null;
        break;
      }
      total = (total ?? 0) + price;
    }
    return total;
  }

  const [intakt24h, intakt7d] = await Promise.all([revenueFor(24), revenueFor(24 * 7)]);

  return {
    mrr,
    intakt_24h: intakt24h,
    intakt_7d: intakt7d,
    valuta: "SEK",
  };
}

async function usersBlock(db: Database): Promise<OpsUsersBlock> {
  const result = await db.execute(sql`
    SELECT
      count(DISTINCT user_id) FILTER (WHERE user_id IS NOT NULL AND occurred_at >= now() - make_interval(mins => 5))::int AS aktiva_nu,
      count(DISTINCT user_id) FILTER (WHERE user_id IS NOT NULL AND occurred_at >= now() - make_interval(hours => 24))::int AS dau,
      count(DISTINCT user_id) FILTER (WHERE user_id IS NOT NULL AND occurred_at >= now() - make_interval(days => 30))::int AS mau,
      count(*) FILTER (WHERE event_name = 'account_created' AND occurred_at >= now() - make_interval(hours => 24))::int AS nya_24h,
      count(*) FILTER (WHERE event_name = 'account_created' AND occurred_at >= now() - make_interval(days => 7))::int AS nya_7d
    FROM product_analytics_events
  `);
  const row = (Array.isArray(result) ? result[0] : result.rows[0]) as {
    aktiva_nu: number;
    dau: number;
    mau: number;
    nya_24h: number;
    nya_7d: number;
  };
  return {
    aktiva_nu: asNumber(row.aktiva_nu),
    dau: asNumber(row.dau),
    mau: asNumber(row.mau),
    nya_24h: asNumber(row.nya_24h),
    nya_7d: asNumber(row.nya_7d),
  };
}

async function subscriptionsBlock(db: Database): Promise<OpsSubscriptionsBlock> {
  const [trials, paying] = await Promise.all([
    db.execute(sql`SELECT count(*)::int AS n FROM trials WHERE ends_at >= now()`),
    db.execute(sql`
      SELECT count(*)::int AS n
      FROM subscriptions
      WHERE status = 'active' AND (expires_at IS NULL OR expires_at > now())
    `),
  ]);
  const trialAktiva = asNumber((Array.isArray(trials) ? trials[0] : trials.rows[0]).n);
  const betalande = asNumber((Array.isArray(paying) ? paying[0] : paying.rows[0]).n);

  const trialConv24h = await db.execute(sql`
    SELECT count(DISTINCT t.user_id)::int AS n
    FROM product_analytics_events t
    INNER JOIN product_analytics_events s ON s.user_id = t.user_id
    WHERE t.event_name = 'trial_started'
      AND s.event_name = 'subscription_started'
      AND s.occurred_at >= now() - make_interval(hours => 24)
      AND s.occurred_at >= t.occurred_at
  `);
  const trialConv7d = await db.execute(sql`
    SELECT count(DISTINCT t.user_id)::int AS n
    FROM product_analytics_events t
    INNER JOIN product_analytics_events s ON s.user_id = t.user_id
    WHERE t.event_name = 'trial_started'
      AND s.event_name = 'subscription_started'
      AND s.occurred_at >= now() - make_interval(days => 7)
      AND s.occurred_at >= t.occurred_at
  `);

  const konv30d = await conversionWithin(db, "trial_started", "subscription_started", 30, 30 * 24);

  const avslutade = await db.execute(sql`
    SELECT count(*)::int AS n
    FROM subscription_events
    WHERE event_type IN ('expired', 'cancelled')
      AND created_at >= now() - make_interval(hours => 24)
  `);

  return {
    trial_aktiva: trialAktiva,
    trial_konverterade_24h: asNumber(
      (Array.isArray(trialConv24h) ? trialConv24h[0] : trialConv24h.rows[0]).n,
    ),
    trial_konverterade_7d: asNumber(
      (Array.isArray(trialConv7d) ? trialConv7d[0] : trialConv7d.rows[0]).n,
    ),
    konverteringsgrad_30d: roundRate(konv30d),
    betalande,
    avslutade_24h: asNumber((Array.isArray(avslutade) ? avslutade[0] : avslutade.rows[0]).n),
  };
}

function storeBlock(): OpsStoreBlock {
  return { butik: null, all_fields_phase_2: null };
}

export function buildWall(summary: OpsSummary): OpsWallBlock {
  const budgetUsd = Number(process.env.GEMINI_DAILY_BUDGET_USD ?? 0);
  const dailySpendUsd =
    summary.ai_scan.budget_andel != null && budgetUsd > 0
      ? summary.ai_scan.budget_andel * budgetUsd
      : null;
  const budgetLabel =
    dailySpendUsd != null && budgetUsd > 0 ? `${dailySpendUsd.toFixed(2)} / ${budgetUsd} USD` : "—";
  const budgetTone: OpsWallTile["tone"] =
    budgetUsd <= 0
      ? "ok"
      : summary.ai_scan.budget_andel == null
        ? "warn"
        : summary.ai_scan.budget_andel >= 1
          ? "crit"
          : summary.ai_scan.budget_andel >= 0.8
            ? "warn"
            : "ok";

  const tiles: OpsWallTile[] = [
    {
      label: "MRR",
      value: formatSekKr(summary.ekonomi.mrr),
      tone: summary.ekonomi.mrr === null ? "warn" : "ok",
    },
    {
      label: "Aktiva nu",
      value: formatInt(summary.anvandare.aktiva_nu),
      tone: "ok",
    },
    {
      label: "Betalande",
      value: formatInt(summary.prenumerationer.betalande),
      tone: "ok",
    },
    {
      label: "Trial-konv",
      value: formatPercent(summary.prenumerationer.konverteringsgrad_30d),
      tone: summary.prenumerationer.konverteringsgrad_30d === null ? "warn" : "ok",
    },
    {
      label: "AI-budget",
      value: budgetLabel,
      tone: budgetTone,
    },
    {
      label: "Workers",
      value: summary.jobb.workers_ok ? "OK" : "NERE",
      tone: summary.jobb.workers_ok ? "ok" : "warn",
    },
    {
      label: "Allergen-brott",
      value: formatInt(summary.sakerhet.allergen_invariant_brott),
      tone: summary.sakerhet.allergen_invariant_brott === 0 ? "ok" : "warn",
    },
  ];

  return {
    boards: [{ title: "{brand}", tiles }],
  };
}

async function feedbackBlock(db: Database): Promise<OpsFeedbackBlock> {
  const counts = await db.execute(sql`
    SELECT
      count(*) FILTER (WHERE status = 'oppen')::int AS oppna,
      count(*) FILTER (WHERE created_at >= now() - make_interval(hours => 24))::int AS nya_24h
    FROM feedback
  `);
  const countsRow = (Array.isArray(counts) ? counts[0] : counts.rows[0]) as {
    oppna: number;
    nya_24h: number;
  };

  const latest = await db
    .select({
      rubrik: schema.feedback.rubrik,
      typ: schema.feedback.typ,
      status: schema.feedback.status,
      createdAt: schema.feedback.createdAt,
    })
    .from(schema.feedback)
    .orderBy(sql`${schema.feedback.createdAt} DESC`)
    .limit(10);

  return {
    oppna: asNumber(countsRow.oppna),
    nya_24h: asNumber(countsRow.nya_24h),
    senaste:
      latest.length > 0
        ? latest.map((f) => ({
            rubrik: f.rubrik,
            typ: f.typ,
            status: f.status,
            created_at: f.createdAt.toISOString(),
          }))
        : null,
  };
}

async function geminiBlock(db: Database): Promise<OpsGeminiBlock> {
  const defaults: OpsGeminiBlock = {
    anrop_24h: null,
    prompt_tokens_24h: null,
    output_tokens_24h: null,
    total_tokens_24h: null,
    total_tokens_manad: null,
    kostnad_usd_mikrocent_24h: null,
    andel_pa_aamos: 0.0,
    aamos_agreement: null,
  };

  try {
    const now = new Date();
    const since24h = new Date(now.getTime() - 24 * 60 * 60 * 1000);
    const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);

    const [dayAgg, monthAgg] = await Promise.all([
      db
        .select({
          count: sql<number>`count(*)`,
          promptTokens: sql<number>`coalesce(sum(${geminiUsage.promptTokens}), 0)`,
          outputTokens: sql<number>`coalesce(sum(${geminiUsage.outputTokens}), 0)`,
          totalTokens: sql<number>`coalesce(sum(${geminiUsage.totalTokens}), 0)`,
          costMicrocents: sql<number>`coalesce(sum(${geminiUsage.costMicrocents}), 0)`,
        })
        .from(geminiUsage)
        .where(gte(geminiUsage.createdAt, since24h)),
      db
        .select({
          totalTokens: sql<number>`coalesce(sum(${geminiUsage.totalTokens}), 0)`,
        })
        .from(geminiUsage)
        .where(gte(geminiUsage.createdAt, startOfMonth)),
    ]);

    return {
      anrop_24h: Number(dayAgg[0]?.count ?? 0),
      prompt_tokens_24h: Number(dayAgg[0]?.promptTokens ?? 0),
      output_tokens_24h: Number(dayAgg[0]?.outputTokens ?? 0),
      total_tokens_24h: Number(dayAgg[0]?.totalTokens ?? 0),
      total_tokens_manad: Number(monthAgg[0]?.totalTokens ?? 0),
      kostnad_usd_mikrocent_24h: Number(dayAgg[0]?.costMicrocents ?? 0),
      andel_pa_aamos: 0.0,
      aamos_agreement: null,
    };
  } catch {
    return defaults;
  }
}

function alarmBlock(budgetUsd: number, dailySpendUsd: number | null | undefined): OpsAlarmBlock {
  const alarms: OpsAlarm[] = [];
  if (budgetUsd > 0 && dailySpendUsd != null && dailySpendUsd >= budgetUsd) {
    alarms.push({
      typ: "budget_exceeded",
      rubrik: "Gemini dagsbudget förbrukad",
      detalj: `Dagens spend ${dailySpendUsd.toFixed(4)} USD når eller överskrider taket ${budgetUsd} USD. Inga nya AI-jobb körs tills imorgon.`,
      allvarlighetsgrad: "crit",
      created_at: new Date().toISOString(),
    });
  }
  return { larm: alarms };
}

export async function computeOpsSummary(options: ComputeOpsSummaryOptions): Promise<OpsSummary> {
  const { db, budgetUsd, dailySpendUsd, queueSummary, planPrices: planPricesOverride } = options;
  const planPrices = { ...getPlanPrices(), ...(planPricesOverride ?? {}) };
  const [
    app,
    economy,
    users,
    subscriptions,
    butik,
    feedbackData,
    ai,
    activation,
    engagement,
    payment,
    safety,
    larm,
    gemini,
  ] = await Promise.all([
    Promise.resolve(appBlock()),
    economyBlock(db, planPrices),
    usersBlock(db),
    subscriptionsBlock(db),
    Promise.resolve(storeBlock()),
    feedbackBlock(db),
    aiScanBlock(db, budgetUsd, dailySpendUsd),
    activationBlock(db),
    engagementBlock(db),
    paymentBlock(db),
    safetyBlock(db),
    Promise.resolve(alarmBlock(budgetUsd, dailySpendUsd)),
    geminiBlock(db),
  ]);

  const base: OpsSummary = {
    app,
    ekonomi: economy,
    anvandare: users,
    prenumerationer: subscriptions,
    butik,
    feedback: feedbackData,
    ai_scan: ai,
    aktivering: activation,
    engagemang: engagement,
    betalning: payment,
    jobb: queueSummary ?? defaultQueueBlock(),
    sakerhet: safety,
    larm,
    wall: { boards: [] },
    gemini,
    as_of: new Date().toISOString(),
  };

  base.wall = buildWall(base);

  return base;
}
