import "./setup-env.js";
import { describe, expect, it, beforeAll, afterAll } from "vitest";
import { buildServer } from "../src/server.js";
import { loadConfig } from "../src/config.js";
import { sql } from "drizzle-orm";
import { createDatabase, schema } from "@app/database";
import { computeOpsSummary, type OpsQueueSummary } from "@app/database/ops-summary";

const testDb = createDatabase(process.env.TEST_DATABASE_URL!);
const config = loadConfig();

const queueSummary: OpsQueueSummary = {
  vantande: 2,
  aktiva: 1,
  misslyckade_24h: 0,
  aldsta_vantande_sek: null,
  workers_ok: true,
};

const planPrices = {
  free: 0,
  household: 9900,
  family: 14900,
  large_household: 19900,
};

describe("/ops/v1/health", () => {
  let app: Awaited<ReturnType<typeof buildServer>>;

  beforeAll(async () => {
    app = await buildServer(config);
    await app.ready();
  });

  afterAll(async () => {
    await app.close();
  });

  it("avvisar anrop utan token", async () => {
    const res = await app.inject({ method: "GET", url: "/ops/v1/health" });
    expect(res.statusCode).toBe(401);
  });

  it("returnerar ok med app-namn vid giltig token", async () => {
    const res = await app.inject({
      method: "GET",
      url: "/ops/v1/health",
      headers: { authorization: `Bearer ${config.OPS_TOKEN}` },
    });
    expect(res.statusCode).toBe(200);
    const body = JSON.parse(res.body) as { ok: boolean; app: string };
    expect(body.ok).toBe(true);
    expect(body.app).toBe("cibello");
  });
});

describe("/ops/v1/summary", () => {
  let app: Awaited<ReturnType<typeof buildServer>>;

  beforeAll(async () => {
    app = await buildServer(config);
    await app.ready();
    await cleanup();
  });

  afterAll(async () => {
    await cleanup();
    await app.close();
    await testDb.pool.end();
  });

  async function cleanup() {
    await testDb.db.delete(schema.opsSafetyCanary);
    await testDb.db.delete(schema.productAnalyticsEvents);
    await testDb.db.delete(schema.feedback);
    await app.redis.del("ops:summary:cache", "ops:summary:computed_at");
  }

  it("avvisar anrop utan token", async () => {
    const res = await app.inject({ method: "GET", url: "/ops/v1/summary" });
    expect(res.statusCode).toBe(401);
  });

  it("avvisar felaktig token", async () => {
    const res = await app.inject({
      method: "GET",
      url: "/ops/v1/summary",
      headers: { authorization: "Bearer wrong-token" },
    });
    expect(res.statusCode).toBe(401);
  });

  it("returnerar 503 när cachen saknas", async () => {
    await app.redis.del("ops:summary:cache", "ops:summary:computed_at");
    const res = await app.inject({
      method: "GET",
      url: "/ops/v1/summary",
      headers: { authorization: `Bearer ${config.OPS_TOKEN}` },
    });
    expect(res.statusCode).toBe(503);
    const body = JSON.parse(res.body) as { error: { code: string } };
    expect(body.error.code).toBe("CACHE_MISS");
  });

  it("serverar cachad summary med rätt block", async () => {
    const now = new Date();
    await testDb.db.insert(schema.productAnalyticsEvents).values([
      { eventName: "scan_started", occurredAt: now, properties: {}, userId: null },
      {
        eventName: "scan_completed",
        occurredAt: now,
        properties: { latencyMs: 120 },
        userId: null,
      },
      {
        eventName: "scan_completed",
        occurredAt: now,
        properties: { latencyMs: 240 },
        userId: null,
      },
      {
        eventName: "cooking_session_completed",
        occurredAt: now,
        properties: {},
        userId: null,
      },
      {
        eventName: "account_created",
        occurredAt: now,
        properties: {},
        userId: null,
      },
      {
        eventName: "account_created",
        occurredAt: now,
        properties: {},
        userId: null,
      },
      {
        eventName: "recommendations_viewed",
        occurredAt: now,
        properties: {},
        userId: null,
      },
      {
        eventName: "recommendation_opened",
        occurredAt: now,
        properties: {},
        userId: null,
      },
    ]);
    await testDb.db.insert(schema.opsSafetyCanary).values({
      allergenInvariantBrott: 0,
      overifieradeVisade: 0,
      foodSafetyLintAvvisade7d: 0,
    });
    await testDb.db.insert(schema.feedback).values([
      {
        userId: null,
        typ: "bug",
        rubrik: "Knappen fungerar inte",
        text: "Detaljerad text – ska aldrig visas i summary.",
        status: "oppen",
      },
      {
        userId: null,
        typ: "onskemal",
        rubrik: "Mörkt läge",
        text: "Önskemålstext – ska aldrig visas i summary.",
        status: "stangd",
      },
    ]);

    const summary = await computeOpsSummary({
      db: testDb.db,
      budgetUsd: 0,
      queueSummary,
      planPrices,
    });
    await app.redis.set("ops:summary:cache", JSON.stringify(summary), "EX", 60);
    await app.redis.set("ops:summary:computed_at", summary.as_of, "EX", 60);

    const res = await app.inject({
      method: "GET",
      url: "/ops/v1/summary",
      headers: { authorization: `Bearer ${config.OPS_TOKEN}` },
    });

    expect(res.statusCode).toBe(200);
    const body = JSON.parse(res.body) as {
      app: { app: string; generated_at: string };
      ekonomi: { mrr: number | null; intakt_24h: number | null; valuta: string };
      anvandare: { aktiva_nu: number; dau: number; mau: number; nya_24h: number; nya_7d: number };
      prenumerationer: { trial_aktiva: number; betalande: number };
      butik: { butik: null };
      feedback: { oppna: number; nya_24h: number; senaste: Array<unknown> };
      ai_scan: { scans_24h: number; latens_p50_ms: number | null };
      engagemang: { lagade_maltider_24h: number };
      sakerhet: { overifierade_visade: number; senaste_kontroll: string | null };
      jobb: { vantande: number; workers_ok: boolean };
      cached_at: string;
    };
    expect(body.app.app).toBe("cibello");
    expect(body.app.generated_at).toBeTruthy();
    expect(body.ekonomi.valuta).toBe("SEK");
    expect(body.anvandare.nya_24h).toBe(2);
    expect(body.anvandare.dau).toBe(0);

    const payingNow = await testDb.db
      .select({ count: sql<number>`count(*)::int` })
      .from(schema.subscriptions)
      .where(
        sql`${schema.subscriptions.status} = 'active' AND (${schema.subscriptions.expiresAt} IS NULL OR ${schema.subscriptions.expiresAt} > now())`,
      );
    expect(body.prenumerationer.betalande).toBe(Number(payingNow[0]?.count ?? 0));

    const trialsNow = await testDb.db
      .select({ count: sql<number>`count(*)::int` })
      .from(schema.trials)
      .where(sql`${schema.trials.endsAt} >= now()`);
    expect(body.prenumerationer.trial_aktiva).toBe(Number(trialsNow[0]?.count ?? 0));

    expect(body.butik.butik).toBeNull();
    expect(body.feedback.oppna).toBe(1);
    expect(body.feedback.nya_24h).toBe(2);
    expect(body.feedback.senaste.length).toBe(2);
    expect(body.feedback.senaste[0]).not.toHaveProperty("text");
    expect(body.ai_scan.scans_24h).toBe(2);
    expect(body.ai_scan.latens_p50_ms).toBeGreaterThan(0);
    expect(body.engagemang.lagade_maltider_24h).toBe(1);
    expect(body.sakerhet.overifierade_visade).toBe(0);
    expect(body.sakerhet.senaste_kontroll).toBeTruthy();
    expect(body.jobb.vantande).toBe(2);
    expect(body.jobb.workers_ok).toBe(true);
    expect(body.cached_at).toBe(summary.as_of);
  });
});
