import { describe, it, expect, beforeEach } from "vitest";
import { get } from "svelte/store";
import { searchState, isSearchActive, searchQuery } from "./search";

describe("searchState store", () => {
  beforeEach(() => {
    searchState.clear();
  });

  describe("initial state", () => {
    it("starts with empty query", () => {
      const state = get(searchState);
      expect(state.query).toBe("");
    });

    it("starts inactive", () => {
      const state = get(searchState);
      expect(state.isActive).toBe(false);
    });

    it("starts with zero match count", () => {
      const state = get(searchState);
      expect(state.matchCount).toBe(0);
    });

    it("starts with zero current match index", () => {
      const state = get(searchState);
      expect(state.currentMatchIndex).toBe(0);
    });
  });

  describe("setQuery", () => {
    it("sets the query string", () => {
      searchState.setQuery("hello");
      expect(get(searchState).query).toBe("hello");
    });

    it("activates the store when query is non-empty", () => {
      searchState.setQuery("hello");
      expect(get(searchState).isActive).toBe(true);
    });

    it("deactivates the store when query is empty", () => {
      searchState.setQuery("hello");
      searchState.setQuery("");
      expect(get(searchState).isActive).toBe(false);
    });

    it("resets currentMatchIndex to 0 on each query change", () => {
      searchState.setQuery("hello");
      searchState.setMatchCount(5);
      searchState.nextMatch();
      searchState.nextMatch();
      searchState.setQuery("world");
      expect(get(searchState).currentMatchIndex).toBe(0);
    });
  });

  describe("setMatchCount", () => {
    it("updates the match count", () => {
      searchState.setMatchCount(7);
      expect(get(searchState).matchCount).toBe(7);
    });

    it("does not reset currentMatchIndex", () => {
      searchState.setQuery("test");
      searchState.setMatchCount(5);
      searchState.nextMatch();
      searchState.setMatchCount(10);
      expect(get(searchState).currentMatchIndex).toBe(1);
    });
  });

  describe("nextMatch", () => {
    it("advances to the next match index", () => {
      searchState.setMatchCount(5);
      searchState.nextMatch();
      expect(get(searchState).currentMatchIndex).toBe(1);
    });

    it("wraps around to 0 after the last match", () => {
      searchState.setMatchCount(3);
      searchState.nextMatch();
      searchState.nextMatch();
      searchState.nextMatch();
      expect(get(searchState).currentMatchIndex).toBe(0);
    });

    it("stays at 0 when match count is 0", () => {
      searchState.setMatchCount(0);
      searchState.nextMatch();
      expect(get(searchState).currentMatchIndex).toBe(0);
    });
  });

  describe("previousMatch", () => {
    it("goes to the previous match index", () => {
      searchState.setMatchCount(5);
      searchState.nextMatch();
      searchState.nextMatch();
      searchState.previousMatch();
      expect(get(searchState).currentMatchIndex).toBe(1);
    });

    it("wraps around to last match when at index 0", () => {
      searchState.setMatchCount(5);
      searchState.previousMatch();
      expect(get(searchState).currentMatchIndex).toBe(4);
    });

    it("stays at 0 when match count is 0", () => {
      searchState.setMatchCount(0);
      searchState.previousMatch();
      expect(get(searchState).currentMatchIndex).toBe(0);
    });
  });

  describe("clear", () => {
    it("resets query to empty string", () => {
      searchState.setQuery("hello");
      searchState.clear();
      expect(get(searchState).query).toBe("");
    });

    it("resets isActive to false", () => {
      searchState.setQuery("hello");
      searchState.clear();
      expect(get(searchState).isActive).toBe(false);
    });

    it("resets matchCount to 0", () => {
      searchState.setMatchCount(10);
      searchState.clear();
      expect(get(searchState).matchCount).toBe(0);
    });

    it("resets currentMatchIndex to 0", () => {
      searchState.setMatchCount(5);
      searchState.nextMatch();
      searchState.nextMatch();
      searchState.clear();
      expect(get(searchState).currentMatchIndex).toBe(0);
    });
  });
});

describe("isSearchActive derived store", () => {
  beforeEach(() => {
    searchState.clear();
  });

  it("is false initially", () => {
    expect(get(isSearchActive)).toBe(false);
  });

  it("is true when query is set", () => {
    searchState.setQuery("test");
    expect(get(isSearchActive)).toBe(true);
  });

  it("is false after clearing", () => {
    searchState.setQuery("test");
    searchState.clear();
    expect(get(isSearchActive)).toBe(false);
  });
});

describe("searchQuery derived store", () => {
  beforeEach(() => {
    searchState.clear();
  });

  it("is empty string initially", () => {
    expect(get(searchQuery)).toBe("");
  });

  it("reflects the current query", () => {
    searchState.setQuery("my search");
    expect(get(searchQuery)).toBe("my search");
  });

  it("is empty after clearing", () => {
    searchState.setQuery("test");
    searchState.clear();
    expect(get(searchQuery)).toBe("");
  });
});
