import { beforeEach, describe, expect, it, vi } from "vitest";

const { createAuditEntry } = vi.hoisted(() => ({
  createAuditEntry: vi.fn(),
}));

vi.mock("../lib/audit.js", () => ({
  createAuditEntry,
}));

import {
  createCountry,
  deleteMetroCity,
  updateCountry,
} from "../router/country-procedure-support.js";

function createContext(db: Record<string, unknown>) {
  return {
    db: db as never,
    dbUser: { id: "user_admin" } as never,
  };
}

describe("country procedure support", () => {
  beforeEach(() => {
    createAuditEntry.mockReset();
  });

  it("creates a country after checking code uniqueness", async () => {
    const findUnique = vi.fn().mockResolvedValue(null);
    const create = vi.fn().mockResolvedValue({
      id: "country_de",
      code: "DE",
      name: "Germany",
      metroCities: [],
    });

    const result = await createCountry(
      createContext({
        country: { findUnique, create },
      }),
      {
        code: "DE",
        name: "Germany",
        dailyWorkingHours: 8,
      },
    );

    expect(findUnique).toHaveBeenCalledWith({
      where: { code: "DE" },
    });
    expect(create).toHaveBeenCalledWith({
      data: {
        code: "DE",
        name: "Germany",
        dailyWorkingHours: 8,
      },
      include: { metroCities: true },
    });
    expect(result.id).toBe("country_de");
    expect(createAuditEntry).toHaveBeenCalledWith(
      expect.objectContaining({
        entityType: "Country",
        action: "CREATE",
        entityId: "country_de",
        userId: "user_admin",
      }),
    );
  });

  it("rechecks code uniqueness only when the code changes", async () => {
    const findUnique = vi
      .fn()
      .mockResolvedValueOnce({
        id: "country_de",
        code: "DE",
        name: "Germany",
      })
      .mockResolvedValueOnce(null);
    const update = vi.fn().mockResolvedValue({
      id: "country_de",
      code: "DEU",
      name: "Germany",
      metroCities: [],
    });

    const result = await updateCountry(
      createContext({
        country: { findUnique, update },
      }),
      {
        id: "country_de",
        data: {
          code: "DEU",
        },
      },
    );

    expect(findUnique).toHaveBeenNthCalledWith(1, {
      where: { id: "country_de" },
    });
    expect(findUnique).toHaveBeenNthCalledWith(2, {
      where: { code: "DEU" },
    });
    expect(update).toHaveBeenCalledWith({
      where: { id: "country_de" },
      data: { code: "DEU" },
      include: { metroCities: true },
    });
    expect(result.code).toBe("DEU");
    expect(createAuditEntry).toHaveBeenCalledWith(
      expect.objectContaining({
        entityType: "Country",
        action: "UPDATE",
        before: expect.objectContaining({ code: "DE" }),
        after: expect.objectContaining({ code: "DEU" }),
      }),
    );
  });

  it("deletes a metro city only after the resource-count guard passes", async () => {
    const findUnique = vi.fn().mockResolvedValue({
      id: "city_berlin",
      name: "Berlin",
      _count: { resources: 0 },
    });
    const remove = vi.fn().mockResolvedValue({ id: "city_berlin" });

    const result = await deleteMetroCity(
      createContext({
        metroCity: { findUnique, delete: remove },
      }),
      { id: "city_berlin" },
    );

    expect(findUnique).toHaveBeenCalledWith({
      where: { id: "city_berlin" },
      include: { _count: { select: { resources: true } } },
    });
    expect(remove).toHaveBeenCalledWith({
      where: { id: "city_berlin" },
    });
    expect(result).toEqual({
      success: true,
      id: "city_berlin",
      name: "Berlin",
    });
    expect(createAuditEntry).toHaveBeenCalledWith(
      expect.objectContaining({
        entityType: "MetroCity",
        action: "DELETE",
        entityId: "city_berlin",
      }),
    );
  });
});
