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

import { countryForTerritory, resolvePostalCode } from "./postalCode";

describe("countryForTerritory", () => {
  it("maps known territory ids and names to ISO country codes", () => {
    expect(countryForTerritory("uk")).toBe("GB");
    expect(countryForTerritory("ireland")).toBe("IE");
    expect(countryForTerritory("france")).toBe("FR");
    expect(countryForTerritory("United-States")).toBe("US");
  });

  it("accepts a territory id that is itself an ISO code", () => {
    expect(countryForTerritory("DE")).toBe("DE");
    expect(countryForTerritory("nl")).toBe("NL");
  });

  it("defaults to GB when null or unrecognised (UK-first, online tiles)", () => {
    expect(countryForTerritory(null)).toBe("GB");
    expect(countryForTerritory(undefined)).toBe("GB");
    expect(countryForTerritory("atlantis")).toBe("GB");
  });
});

function zippoResponse(placeName: string, state: string, lat: string, lng: string): Response {
  return {
    ok: true,
    json: async () => ({
      places: [{ "place name": placeName, state, latitude: lat, longitude: lng }],
    }),
  } as unknown as Response;
}

describe("resolvePostalCode — non-UK via Zippopotam", () => {
  it("resolves a US ZIP to a SearchResult", async () => {
    const fetchImpl = vi.fn(async () => zippoResponse("Beverly Hills", "California", "34.0901", "-118.4065"));
    const result = await resolvePostalCode("90210", "US", undefined, fetchImpl as unknown as typeof fetch);
    expect(fetchImpl).toHaveBeenCalledWith("https://api.zippopotam.us/us/90210");
    expect(result).toMatchObject({
      name: "90210",
      address: "Beverly Hills, California",
      position: { latitude: 34.0901, longitude: -118.4065 },
    });
  });

  it("keys Canadian lookups by the 3-char forward-sortation area", async () => {
    const fetchImpl = vi.fn(async () => zippoResponse("Downtown Toronto", "Ontario", "43.6525", "-79.3686"));
    const result = await resolvePostalCode("M5V 3L9", "CA", undefined, fetchImpl as unknown as typeof fetch);
    expect(fetchImpl).toHaveBeenCalledWith("https://api.zippopotam.us/ca/M5V");
    expect(result).toMatchObject({ name: "M5V", address: "Downtown Toronto, Ontario" });
  });

  it("keys Dutch lookups by the 4-digit part and formats with a space", async () => {
    const fetchImpl = vi.fn(async () => zippoResponse("Amsterdam", "Noord-Holland", "52.3789", "4.9005"));
    const result = await resolvePostalCode("1011AB", "NL", undefined, fetchImpl as unknown as typeof fetch);
    expect(fetchImpl).toHaveBeenCalledWith("https://api.zippopotam.us/nl/1011");
    expect(result).toMatchObject({ name: "1011 AB", address: "Amsterdam, Noord-Holland" });
  });

  it("does not fire the network for a query that isn't a postal code for the country", async () => {
    const fetchImpl = vi.fn();
    // "SW1A 1AA" is a UK postcode, not a German 5-digit code.
    expect(await resolvePostalCode("SW1A 1AA", "DE", undefined, fetchImpl as unknown as typeof fetch)).toBeNull();
    expect(fetchImpl).not.toHaveBeenCalled();
  });

  it("returns null for a country with no free coverage (IE)", async () => {
    const fetchImpl = vi.fn();
    expect(await resolvePostalCode("D01 F5P2", "IE", undefined, fetchImpl as unknown as typeof fetch)).toBeNull();
    expect(fetchImpl).not.toHaveBeenCalled();
  });

  it("delegates GB to the postcodes.io path", async () => {
    const fetchImpl = vi.fn(async () =>
      ({ ok: true, json: async () => ({ result: { postcode: "BN11 1AA", latitude: 50.81, longitude: -0.37, admin_district: "Worthing" } }) }) as unknown as Response
    );
    const result = await resolvePostalCode("BN11 1AA", "GB", undefined, fetchImpl as unknown as typeof fetch);
    expect(fetchImpl).toHaveBeenCalledWith("https://api.postcodes.io/postcodes/BN111AA");
    expect(result).toMatchObject({ name: "BN11 1AA" });
  });

  it("swallows network errors and returns null", async () => {
    const fetchImpl = vi.fn(async () => {
      throw new Error("offline");
    });
    expect(await resolvePostalCode("90210", "US", undefined, fetchImpl as unknown as typeof fetch)).toBeNull();
  });
});
