import { describe, expect, it, beforeEach, vi } from "vitest";
import { z } from "zod";
import { storage } from "./storage.js";

const schema = z.object({ name: z.string(), age: z.number() });
type Person = z.infer<typeof schema>;

describe("storage", () => {
  let store: Map<string, string>;

  beforeEach(() => {
    store = new Map();
    const mockStorage = {
      getItem: vi.fn((key: string) => store.get(key) ?? null),
      setItem: vi.fn((key: string, value: string) => {
        store.set(key, value);
      }),
      removeItem: vi.fn((key: string) => {
        store.delete(key);
      }),
      get length() {
        return store.size;
      },
      key: vi.fn((index: number) => [...store.keys()][index] ?? null),
      clear: vi.fn(() => store.clear()),
    } satisfies Storage;

    vi.stubGlobal("localStorage", mockStorage);
  });

  it("round-trips get/set with a valid schema", () => {
    const value: Person = { name: "Alice", age: 30 };
    storage.set("user", value, schema);
    expect(storage.get("user", schema)).toEqual(value);
  });

  it("returns null for a missing key", () => {
    expect(storage.get("nonexistent", schema)).toBeNull();
  });

  it("returns null when stored value fails schema validation", () => {
    // Store invalid data directly
    localStorage.setItem("afl_bad", JSON.stringify({ name: 123, age: "wrong" }));
    expect(storage.get("bad", schema)).toBeNull();
  });

  it("returns null when stored value is not valid JSON", () => {
    localStorage.setItem("afl_corrupt", "not-json{{{");
    expect(storage.get("corrupt", schema)).toBeNull();
  });

  it("throws when set value does not match schema", () => {
    const invalid = { name: 123, age: "wrong" } as unknown as Person;
    expect(() => storage.set("user", invalid, schema)).toThrow();
  });

  it("deletes a key", () => {
    storage.set("user", { name: "Alice", age: 30 }, schema);
    storage.delete("user");
    expect(storage.get("user", schema)).toBeNull();
  });

  it("clear removes only afl_-prefixed keys", () => {
    storage.set("user", { name: "Alice", age: 30 }, schema);
    localStorage.setItem("other_key", "keep me");
    storage.clear();
    expect(storage.get("user", schema)).toBeNull();
    expect(localStorage.getItem("other_key")).toBe("keep me");
  });

  it("stores keys with afl_ prefix", () => {
    storage.set("user", { name: "Alice", age: 30 }, schema);
    expect(localStorage.getItem("afl_user")).not.toBeNull();
    expect(localStorage.getItem("user")).toBeNull();
  });
});
