import "./setup-env.js";
import { describe, expect, it, beforeAll, afterAll } from "vitest";
import { eq, inArray } from "drizzle-orm";
import { buildServer } from "../src/server.js";
import { loadConfig } from "../src/config.js";
import { createDatabase, closeDatabase, schema } from "@app/database";

describe("scan confirmation → ai_corrections", () => {
  const testDb = createDatabase(process.env.TEST_DATABASE_URL!);
  const config = loadConfig();
  let app: Awaited<ReturnType<typeof buildServer>>;
  let token: string;
  let userId: string;
  let householdId: string;
  let locationId: string;
  const email = "scan-confirm-test@example.invalid";

  async function cleanup() {
    const existing = await testDb.db
      .select({ id: schema.users.id })
      .from(schema.users)
      .where(inArray(schema.users.email, [email]));
    for (const u of existing) {
      await testDb.db.delete(schema.aiCorrections).where(eq(schema.aiCorrections.userId, u.id));
      await testDb.db.delete(schema.idempotencyKeys).where(eq(schema.idempotencyKeys.userId, u.id));
      const memberships = await testDb.db
        .select({ householdId: schema.householdMembers.householdId })
        .from(schema.householdMembers)
        .where(eq(schema.householdMembers.userId, u.id));
      for (const m of memberships) {
        const items = await testDb.db
          .select({ id: schema.inventoryItems.id })
          .from(schema.inventoryItems)
          .where(eq(schema.inventoryItems.householdId, m.householdId));
        for (const it of items) {
          await testDb.db
            .delete(schema.inventoryTransactions)
            .where(eq(schema.inventoryTransactions.inventoryItemId, it.id));
          await testDb.db
            .delete(schema.inventoryConflicts)
            .where(eq(schema.inventoryConflicts.inventoryItemId, it.id));
        }
        await testDb.db
          .delete(schema.inventoryItems)
          .where(eq(schema.inventoryItems.householdId, m.householdId));
        await testDb.db
          .delete(schema.inventoryConflicts)
          .where(eq(schema.inventoryConflicts.householdId, m.householdId));
        await testDb.db
          .delete(schema.storageLocations)
          .where(eq(schema.storageLocations.householdId, m.householdId));
        await testDb.db
          .delete(schema.householdMembers)
          .where(eq(schema.householdMembers.householdId, m.householdId));
        await testDb.db.delete(schema.households).where(eq(schema.households.id, m.householdId));
      }
      await testDb.db.delete(schema.scanJobs).where(eq(schema.scanJobs.userId, u.id));
      await testDb.db.delete(schema.userConsents).where(eq(schema.userConsents.userId, u.id));
      await testDb.db.delete(schema.userPreferences).where(eq(schema.userPreferences.userId, u.id));
      await testDb.db.delete(schema.users).where(eq(schema.users.id, u.id));
    }
  }

  async function setConsent(imageTraining: boolean) {
    const kinds = ["personalization", "anonymized_improvement"] as const;
    for (const kind of kinds) {
      await testDb.db
        .insert(schema.userConsents)
        .values({ userId, kind, status: "granted" as const })
        .onConflictDoUpdate({
          target: [schema.userConsents.userId, schema.userConsents.kind],
          set: { status: "granted" as const },
        });
    }
    await testDb.db
      .insert(schema.userConsents)
      .values({
        userId,
        kind: "image_training" as const,
        status: (imageTraining ? "granted" : "denied") as "granted" | "denied",
      })
      .onConflictDoUpdate({
        target: [schema.userConsents.userId, schema.userConsents.kind],
        set: { status: (imageTraining ? "granted" : "denied") as "granted" | "denied" },
      });
  }

  async function createScanJob() {
    const [job] = await testDb.db
      .insert(schema.scanJobs)
      .values({
        userId,
        householdId,
        scanType: "fridge",
        jobType: "ANALYZE_FRIDGE_IMAGE",
        status: "awaiting_confirmation",
        s3Keys: ["fridge-scans/test-image.jpg"],
        result: {
          items: [
            {
              tempId: "item-1",
              detectedName: "Mellanmjölk",
              canonicalIngredientId: "milk_1_5",
              brand: "Arla",
              estimatedQuantity: 1,
              unit: "LITER",
              confidence: 0.98,
              requiresConfirmation: false,
            },
          ],
        },
        modelVersion: "gemini-3.1-flash-lite",
        promptVersion: "gemini-fridge-v1",
      })
      .returning();
    return job!.id;
  }

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

    const res = await app.inject({
      method: "POST",
      url: "/v1/auth/register",
      payload: { email, password: "Password123!", displayName: "Scan Confirm Test" },
    });
    const body = JSON.parse(res.body) as { accessToken: string };
    token = body.accessToken;
    userId = (JSON.parse(atob(token.split(".")[1]!)) as { sub: string }).sub;

    const quick = await app.inject({
      method: "POST",
      url: "/v1/onboarding/quick-start",
      headers: { authorization: `Bearer ${token}` },
      payload: { goals: ["less_waste"], precisionMode: "simple" },
    });
    householdId = (JSON.parse(quick.body) as { householdId: string }).householdId;

    const [location] = await testDb.db
      .select({ id: schema.storageLocations.id })
      .from(schema.storageLocations)
      .where(eq(schema.storageLocations.householdId, householdId))
      .limit(1);
    locationId = location!.id;
  });

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

  it("accept action writes a positive row in ai_corrections", async () => {
    await setConsent(false);
    const scanJobId = await createScanJob();

    const res = await app.inject({
      method: "POST",
      url: `/v1/scans/${scanJobId}/confirm`,
      headers: { authorization: `Bearer ${token}` },
      payload: {
        items: [
          {
            tempId: "item-1",
            action: "accept",
            displayName: "Mellanmjölk",
            canonicalIngredientId: "milk_1_5",
            brand: "Arla",
            quantity: 1,
            unit: "LITER",
            storageLocationId: locationId,
          },
        ],
      },
    });
    expect(res.statusCode).toBe(200);

    const corrections = await testDb.db
      .select()
      .from(schema.aiCorrections)
      .where(eq(schema.aiCorrections.scanJobId, scanJobId));
    expect(corrections).toHaveLength(1);
    expect((corrections[0]!.userCorrection as Record<string, string>).action).toBe("accept");
    expect((corrections[0]!.proposal as Record<string, unknown>).detectedName).toBe("Mellanmjölk");
    expect(
      (corrections[0]!.userCorrection as Record<string, Record<string, unknown>>).corrected,
    ).toMatchObject({
      displayName: "Mellanmjölk",
      quantity: 1,
      unit: "LITER",
    });
  });

  it("saves image reference when image_training consent is granted", async () => {
    await setConsent(true);
    const scanJobId = await createScanJob();

    const res = await app.inject({
      method: "POST",
      url: `/v1/scans/${scanJobId}/confirm`,
      headers: { authorization: `Bearer ${token}` },
      payload: {
        items: [
          {
            tempId: "item-1",
            action: "accept",
            displayName: "Mellanmjölk",
            quantity: 1,
            unit: "LITER",
            storageLocationId: locationId,
          },
        ],
      },
    });
    expect(res.statusCode).toBe(200);

    const corrections = await testDb.db
      .select()
      .from(schema.aiCorrections)
      .where(eq(schema.aiCorrections.scanJobId, scanJobId));
    expect(corrections[0]!.imageS3Key).toBe("fridge-scans/test-image.jpg");
    expect((corrections[0]!.consentSnapshot as Record<string, string>).image_training).toBe(
      "granted",
    );
  });

  it("does not save image reference when image_training consent is denied", async () => {
    await setConsent(false);
    const scanJobId = await createScanJob();

    const res = await app.inject({
      method: "POST",
      url: `/v1/scans/${scanJobId}/confirm`,
      headers: { authorization: `Bearer ${token}` },
      payload: {
        items: [
          {
            tempId: "item-1",
            action: "accept",
            displayName: "Mellanmjölk",
            quantity: 1,
            unit: "LITER",
            storageLocationId: locationId,
          },
        ],
      },
    });
    expect(res.statusCode).toBe(200);

    const corrections = await testDb.db
      .select()
      .from(schema.aiCorrections)
      .where(eq(schema.aiCorrections.scanJobId, scanJobId));
    expect(corrections[0]!.imageS3Key).toBeNull();
  });
});
