import { describe, it, expect, beforeAll, afterAll } from "vitest";
import Fastify from "fastify";
import jwt from "jsonwebtoken";
import companySettingsRoutes from "../routes/admin/company-settings";
import prisma from "../config/database";
import { config } from "../config/env";

// Fixture prefix so cleanup never touches real data.
const N = "settings_numbering_test_";

let app: ReturnType<typeof Fastify>;
let token: string;

// Snapshot the prior values of the four numbering fields we mutate so we can
// restore them in afterAll — other tests read company_settings and must not be
// poisoned by our writes.
let priorValues: {
  issued_order_number_pattern: string | null;
  issued_order_type_code: string | null;
  project_number_pattern: string | null;
  project_type_code: string | null;
} | null = null;
let settingsId: number | null = null;

beforeAll(async () => {
  app = Fastify({ logger: false });
  await app.register(companySettingsRoutes, {
    prefix: "/api/admin/company-settings",
  });

  // Clean up any leftover fixture from a previous failed run.
  await prisma.users
    .deleteMany({ where: { username: { startsWith: N } } })
    .catch(() => {});

  // Attach the fixture user to the admin role so the PUT/GET permission guards
  // (settings.company / settings.system) pass.
  const adminRole = await prisma.roles.findFirst({ where: { name: "admin" } });
  if (!adminRole)
    throw new Error("Test setup: no admin role found — seed the database");

  const user = await prisma.users.create({
    data: {
      username: `${N}user`,
      email: `${N}user@test.local`,
      password_hash: "x",
      first_name: "Settings",
      last_name: "Test",
      is_active: true,
      totp_enabled: false,
      role_id: adminRole.id,
    },
  });
  token = jwt.sign(
    { sub: user.id, username: user.username, role: "admin" },
    config.jwt.secret,
    { expiresIn: config.jwt.accessTokenExpiry },
  );

  // Snapshot existing settings so we can restore after the suite.
  const existing = await prisma.company_settings.findFirst({
    select: {
      id: true,
      issued_order_number_pattern: true,
      issued_order_type_code: true,
      project_number_pattern: true,
      project_type_code: true,
    },
  });
  if (existing) {
    settingsId = existing.id;
    priorValues = {
      issued_order_number_pattern: existing.issued_order_number_pattern,
      issued_order_type_code: existing.issued_order_type_code,
      project_number_pattern: existing.project_number_pattern,
      project_type_code: existing.project_type_code,
    };
  }
});

afterAll(async () => {
  // Restore the four numbering fields to their pre-test values.
  if (settingsId !== null && priorValues !== null) {
    await prisma.company_settings
      .update({ where: { id: settingsId }, data: priorValues })
      .catch(() => {});
  }
  await prisma.users
    .deleteMany({ where: { username: { startsWith: N } } })
    .catch(() => {});
  await app.close();
});

describe("company-settings numbering — issued orders + projects round-trip", () => {
  it("persists and returns issued_order_* and project_* numbering fields", async () => {
    const payload = {
      issued_order_number_pattern: "{YY}V{NNNN}",
      issued_order_type_code: "72",
      project_number_pattern: "{YY}P{NNNN}",
      project_type_code: "73",
    };

    const put = await app.inject({
      method: "PUT",
      url: "/api/admin/company-settings/",
      headers: { authorization: `Bearer ${token}` },
      payload,
    });
    expect(put.statusCode).toBe(200);
    expect(put.json().success).toBe(true);

    const get = await app.inject({
      method: "GET",
      url: "/api/admin/company-settings/",
      headers: { authorization: `Bearer ${token}` },
    });
    expect(get.statusCode).toBe(200);
    const data = get.json().data;
    expect(data.issued_order_number_pattern).toBe("{YY}V{NNNN}");
    expect(data.issued_order_type_code).toBe("72");
    expect(data.project_number_pattern).toBe("{YY}P{NNNN}");
    expect(data.project_type_code).toBe("73");
  });

  it("requires authentication", async () => {
    const res = await app.inject({
      method: "GET",
      url: "/api/admin/company-settings/",
    });
    expect(res.statusCode).toBe(401);
  });
});
