import { describe, expect, it } from "vitest";
import {
  addSavedPlace,
  getFavourites,
  getHome,
  getWork,
  makeSavedId,
  removeSavedPlace,
} from "./reducer";
import type { SavedPlace } from "./reducer";
import type { SearchResult } from "../types";

const home1: SearchResult = {
  name: "12 Elm Street",
  address: "London N1",
  position: { latitude: 51.5, longitude: -0.1 },
};

const home2: SearchResult = {
  name: "45 Oak Avenue",
  address: "London N2",
  position: { latitude: 51.55, longitude: -0.12 },
};

const work1: SearchResult = {
  name: "Precode HQ",
  address: "London EC1",
  position: { latitude: 51.52, longitude: -0.09 },
};

const cafe: SearchResult = {
  name: "Blue Bottle Coffee",
  address: "London E2",
  position: { latitude: 51.53, longitude: -0.06 },
};

const bakery: SearchResult = {
  name: "Corner Bakery",
  address: "London E3",
  position: { latitude: 51.54, longitude: -0.05 },
};

describe("makeSavedId", () => {
  it("is deterministic and singleton-keyed for home/work", () => {
    expect(makeSavedId("home", home1)).toBe("home");
    expect(makeSavedId("home", home2)).toBe("home");
    expect(makeSavedId("work", work1)).toBe("work");
  });

  it("is deterministic per name+coordinates for favourites, without Date.now/Math.random", () => {
    const first = makeSavedId("favourite", cafe);
    const second = makeSavedId("favourite", cafe);
    expect(first).toBe(second);
    expect(makeSavedId("favourite", cafe)).not.toBe(makeSavedId("favourite", bakery));
  });
});

describe("addSavedPlace", () => {
  it("adds a home entry with a default label", () => {
    const next = addSavedPlace([], home1, "home");
    expect(next).toHaveLength(1);
    expect(next[0]).toMatchObject({ id: "home", kind: "home", label: "Home", name: home1.name });
  });

  it("replaces the existing home when a new one is set (singleton)", () => {
    const withFirstHome = addSavedPlace([], home1, "home");
    const withSecondHome = addSavedPlace(withFirstHome, home2, "home");
    expect(withSecondHome).toHaveLength(1);
    expect(withSecondHome[0].name).toBe(home2.name);
    expect(getHome(withSecondHome)?.name).toBe(home2.name);
  });

  it("replaces the existing work when a new one is set (singleton), independent of home", () => {
    const seeded = addSavedPlace(addSavedPlace([], home1, "home"), work1, "work");
    const rehomed = addSavedPlace(seeded, home2, "home");
    expect(rehomed).toHaveLength(2);
    expect(getHome(rehomed)?.name).toBe(home2.name);
    expect(getWork(rehomed)?.name).toBe(work1.name);
  });

  it("accepts a custom label", () => {
    const next = addSavedPlace([], home1, "home", "Mum's house");
    expect(next[0].label).toBe("Mum's house");
  });

  it("appends favourites rather than replacing them", () => {
    const next = addSavedPlace(addSavedPlace([], cafe, "favourite"), bakery, "favourite");
    expect(getFavourites(next)).toHaveLength(2);
  });

  it("re-adding the same favourite updates it in place instead of duplicating", () => {
    const first = addSavedPlace([], cafe, "favourite");
    const second = addSavedPlace(first, cafe, "favourite", "My coffee spot");
    expect(getFavourites(second)).toHaveLength(1);
    expect(getFavourites(second)[0].label).toBe("My coffee spot");
  });
});

describe("removeSavedPlace", () => {
  it("removes by id and leaves other entries untouched", () => {
    const seeded = addSavedPlace(
      addSavedPlace(addSavedPlace([], home1, "home"), work1, "work"),
      cafe,
      "favourite"
    );
    const favouriteId = getFavourites(seeded)[0].id;
    const next = removeSavedPlace(seeded, favouriteId);
    expect(next).toHaveLength(2);
    expect(getFavourites(next)).toHaveLength(0);
    expect(getHome(next)?.name).toBe(home1.name);
    expect(getWork(next)?.name).toBe(work1.name);
  });

  it("removing an unknown id is a no-op", () => {
    const seeded: SavedPlace[] = addSavedPlace([], home1, "home");
    expect(removeSavedPlace(seeded, "does-not-exist")).toEqual(seeded);
  });
});
