import { describe, it, expect, vi, beforeEach } from "vitest";
import { NextRequest } from "next/server";

// ── Hoisted mocks ────────────────────────────────────────────────────────────
const { mockDbExecute } = vi.hoisted(() => ({
  mockDbExecute: vi.fn(),
}));

vi.mock("@/app/db/db", () => ({
  db: {
    get execute() {
      return mockDbExecute;
    },
  },
}));

import { GET } from "./route";

// ── Helpers ──────────────────────────────────────────────────────────────────

function makeRequest(query = "") {
  return new NextRequest(
    `http://localhost/api/portfolio/785/scenario/12/metrics${query}`,
  );
}

function makeProps(scenarioId = "12") {
  return { params: Promise.resolve({ portfolioId: "785", scenarioId }) };
}

/**
 * Queue the route's queries. The scenario definition resolves first; the three
 * data queries (plans metrics, upgraded, portfolio-after) then run in one
 * Promise.all wave, so their mocks are consumed in that array order. The
 * portfolio-after query now also carries the EPC-band buckets (band_*), folded
 * in from the old separate distribution scan.
 */
function queueHappyPath({
  goal = "Increasing EPC",
  goalValue = "C",
  upgraded = {
    n_units_upgraded: 100,
    total_cost: 1_000_000,
    contingency: 100_000,
    total_funding: 200_000,
  },
} = {}) {
  mockDbExecute
    .mockResolvedValueOnce({ rows: [{ goal, goal_value: goalValue }] })
    .mockResolvedValueOnce({
      rows: [
        {
          n_units: 100,
          avg_sap: 71,
          avg_carbon: 1.9,
          avg_bills: 941,
          total_carbon: 593,
          total_bills: 294_000,
          total_sap_uplift: 2_834,
        },
      ],
    })
    .mockResolvedValueOnce({ rows: [upgraded] })
    .mockResolvedValueOnce({
      rows: [
        {
          avg_sap: 71,
          avg_carbon: 1.9,
          avg_bills: 941,
          total_carbon: 593,
          total_bills: 294_000,
          band_a: 0,
          band_b: 10,
          band_c: 60,
          band_d: 20,
          band_e: 8,
          band_f: 2,
          band_g: 0,
          band_unknown: 0,
        },
      ],
    });
}

beforeEach(() => {
  mockDbExecute.mockReset();
});

// ── Tests ────────────────────────────────────────────────────────────────────

describe("GET scenario metrics", () => {
  it("rejects an invalid scenarioId", async () => {
    const res = await GET(makeRequest(), makeProps("null"));
    expect(res.status).toBe(400);
    expect(await res.json()).toEqual({ error: "Invalid scenarioId" });
  });

  it("404s an unknown scenario", async () => {
    mockDbExecute.mockResolvedValueOnce({ rows: [] });
    const res = await GET(makeRequest(), makeProps());
    expect(res.status).toBe(404);
    expect(await res.json()).toEqual({ error: "Scenario not found" });
  });

  it("reports the canonical all-in gross and net (CONTEXT.md definitions)", async () => {
    queueHappyPath();
    const res = await GET(makeRequest(), makeProps());
    expect(res.status).toBe(200);
    const body = await res.json();

    // gross = construction + delivery(30%) + contingency; net = gross − funding
    expect(body.construction_cost).toBe(1_000_000);
    expect(body.pc_cost).toBe(300_000);
    expect(body.contingency).toBe(100_000);
    expect(body.total_funding).toBe(200_000);
    expect(body.gross_cost).toBe(1_400_000);
    expect(body.net_cost).toBe(1_200_000);
    expect(body.gross_per_unit).toBe(14_000);
  });

  it("passes the SQL-bucketed EPC-band counts straight through", async () => {
    queueHappyPath();
    const res = await GET(makeRequest(), makeProps());
    const body = await res.json();

    // The band_* columns from the merged portfolio-after query map onto the
    // scenario_epc_counts the ladder consumes — no JS re-bucketing.
    expect(body.scenario_epc_counts).toEqual({
      A: 0,
      B: 10,
      C: 60,
      D: 20,
      E: 8,
      F: 2,
      G: 0,
      Unknown: 0,
    });
  });

  // Compliance window — ADR-0010: report-view parameter, configurable band + date.

  it("rejects a compliance band outside A–G", async () => {
    const res = await GET(
      makeRequest("?complianceDate=2030-01-01&complianceBand=Z"),
      makeProps(),
    );
    expect(res.status).toBe(400);
    expect(await res.json()).toEqual({ error: "Invalid complianceBand" });
  });

  it("rejects an unparseable compliance date", async () => {
    const res = await GET(
      makeRequest("?complianceDate=soon"),
      makeProps(),
    );
    expect(res.status).toBe(400);
    expect(await res.json()).toEqual({ error: "Invalid complianceDate" });
  });

  it("accepts a valid compliance window, defaulting the band to C", async () => {
    queueHappyPath();
    const res = await GET(makeRequest("?complianceDate=2030-01-01"), makeProps());
    expect(res.status).toBe(200);
  });
});
