// #region ValidationTasksListModelTests [C:3] [TYPE Module]
// @BRIEF L1 unit tests for ValidationTasksListModel — no DOM render.
// @RELATION BINDS_TO -> [ValidationTasksListModel]
import { describe, it, expect, vi, beforeEach } from "vitest";
vi.mock("$app/navigation", () => ({ goto: vi.fn() }));
vi.mock("$lib/api.js", () => ({
  api: {
    getValidationTasks: vi.fn(),
    deleteValidationTask: vi.fn(),
    toggleValidationTaskStatus: vi.fn(),
    triggerValidationRun: vi.fn(),
  },
}));
vi.mock("$lib/cot-logger", () => ({ log: vi.fn() }));
vi.mock("$lib/toasts.svelte.js", () => ({ addToast: vi.fn() }));
vi.mock("$lib/i18n/index.svelte.js", () => ({ getT: () => ({ validation: { run_started: "Started", delete_success: "Deleted" } }) }));
vi.mock("$lib/routes.js", () => ({ ROUTES: { validationTasks: { new: vi.fn(() => "/validation/new"), detail: vi.fn((id) => `/validation/${id}`), edit: vi.fn((id) => `/validation/${id}/edit`) } } }));
import { ValidationTasksListModel } from "../ValidationTasksListModel.svelte.ts";
import { api } from "$lib/api.js";
import { goto } from "$app/navigation";
import { addToast } from "$lib/toasts.svelte.js";

// #region ValidationTasksListModel.InvariantTests [C:2] [TYPE Function]
describe("ValidationTasksListModel — L1 invariants", () => {
  let model: ValidationTasksListModel;
  beforeEach(() => {
    vi.clearAllMocks();
    vi.useFakeTimers();
    model = new ValidationTasksListModel();
  });
  afterEach(() => { vi.useRealTimers(); });

  // #region ValidationTasksListModel.InitialState [C:2] [TYPE Test]
  it("starts with empty tasks", () => {
    expect(model.tasks).toEqual([]);
    expect(model.currentPage).toBe(1);
    expect(model.isMounted).toBe(true);
  });
  // #endregion

  // #region ValidationTasksListModel.TotalPages [C:2] [TYPE Test]
  it("totalPages is at least 1", () => {
    expect(model.totalPages).toBe(1);
    model.total = 0;
    expect(model.totalPages).toBe(1);
  });
  // #endregion

  // #region ValidationTasksListModel.SearchDebounce [C:2] [TYPE Test]
  it("handleSearchInput resets currentPage via debounce", () => {
    model.currentPage = 5;
    model.handleSearchInput("test");
    expect(model.currentPage).toBe(5);
    vi.advanceTimersByTime(400);
    expect(model.currentPage).toBe(1);
  });
  // #endregion

  // #region ValidationTasksListModel.GoToPage [C:2] [TYPE Test]
  it("goToPage navigates to valid page", () => {
    model.total = 40; model.pageSize = 20;
    model.goToPage(2);
    expect(model.currentPage).toBe(2);
  });

  it("goToPage ignores page < 1", () => {
    model.currentPage = 3;
    model.goToPage(0);
    expect(model.currentPage).toBe(3);
  });

  it("goToPage ignores page > totalPages", () => {
    model.currentPage = 1;
    model.goToPage(99);
    expect(model.currentPage).toBe(1);
  });
  // #endregion

  // #region ValidationTasksListModel.Navigation [C:2] [TYPE Test]
  it("navToNew navigates to new task route", () => {
    model.navToNew();
    expect(goto).toHaveBeenCalledWith("/validation/new", { invalidateAll: false });
  });

  it("navToDetail navigates to detail route", () => {
    model.navToDetail("t1");
    expect(goto).toHaveBeenCalledWith("/validation/t1", { invalidateAll: false });
  });

  it("navToEdit navigates to edit route", () => {
    model.navToEdit("t1");
    expect(goto).toHaveBeenCalledWith("/validation/t1/edit", { invalidateAll: false });
  });
  // #endregion

  // #region ValidationTasksListModel.Destroy [C:2] [TYPE Test]
  it("destroy sets isMounted to false", () => {
    model.destroy();
    expect(model.isMounted).toBe(false);
  });
  // #endregion
});
// #endregion

// #region ValidationTasksListModel.DataLoading [C:2] [TYPE Function]
describe("ValidationTasksListModel — Data Loading", () => {
  let model: ValidationTasksListModel;
  beforeEach(() => {
    vi.clearAllMocks();
    vi.useFakeTimers();
    model = new ValidationTasksListModel();
  });
  afterEach(() => { vi.useRealTimers(); });

  // #region ValidationTasksListModel.LoadTasks [C:2] [TYPE Test]
  it("loadTasks populates tasks on success", async () => {
    vi.mocked(api.getValidationTasks).mockResolvedValue({ tasks: [{ id: "t1", name: "Task 1" }], total: 1 });
    await model.loadTasks();
    expect(model.tasks).toHaveLength(1);
    expect(model.isLoading).toBe(false);
  });

  it("loadTasks handles tasks in results format", async () => {
    vi.mocked(api.getValidationTasks).mockResolvedValue({ results: [{ id: "t1" }], total: 1 });
    await model.loadTasks();
    expect(model.tasks).toHaveLength(1);
  });

  it("loadTasks handles error", async () => {
    vi.mocked(api.getValidationTasks).mockRejectedValue(new Error("API error"));
    await model.loadTasks();
    expect(model.error).toBe("API error");
    expect(model.isLoading).toBe(false);
  });

  it("loadTasks returns early if unmounted", async () => {
    model.destroy();
    vi.mocked(api.getValidationTasks).mockRejectedValue(new Error("Error"));
    await model.loadTasks();
    expect(model.error).toBeNull();
  });

  it("loadTasks applies search query param", async () => {
    model.searchQuery = "test";
    vi.mocked(api.getValidationTasks).mockResolvedValue({ tasks: [], total: 0 });
    await model.loadTasks();
    expect(api.getValidationTasks).toHaveBeenCalledWith(
      expect.objectContaining({ search: "test" }),
    );
  });
  // #endregion
});
// #endregion

// #region ValidationTasksListModel.CrudActions [C:2] [TYPE Function]
describe("ValidationTasksListModel — CRUD Actions", () => {
  let model: ValidationTasksListModel;
  beforeEach(() => {
    vi.clearAllMocks();
    model = new ValidationTasksListModel();
  });

  // #region ValidationTasksListModel.HandleRunNow [C:2] [TYPE Test]
  it("handleRunNow calls trigger API and shows toast", async () => {
    vi.mocked(api.triggerValidationRun).mockResolvedValue({});
    await model.handleRunNow("t1");
    expect(api.triggerValidationRun).toHaveBeenCalledWith("t1");
    expect(addToast).toHaveBeenCalledWith("Started", "success");
    expect(model.runningTasks["t1"]).toBe(false);
  });

  it("handleRunNow handles error", async () => {
    vi.mocked(api.triggerValidationRun).mockRejectedValue(new Error("Run failed"));
    await model.handleRunNow("t1");
    expect(addToast).toHaveBeenCalledWith("Run failed", "error");
    expect(model.runningTasks["t1"]).toBe(false);
  });

  it("handleRunNow returns early if unmounted", async () => {
    model.destroy();
    vi.mocked(api.triggerValidationRun).mockRejectedValue(new Error("Error"));
    await model.handleRunNow("t1");
    expect(addToast).not.toHaveBeenCalled();
  });
  // #endregion

  // #region ValidationTasksListModel.HandleDelete [C:2] [TYPE Test]
  it("handleDelete removes task and decrements total", async () => {
    vi.mocked(api.deleteValidationTask).mockResolvedValue({});
    model.tasks = [{ id: "t1", name: "Task 1" } as any, { id: "t2", name: "Task 2" } as any];
    model.total = 2;
    await model.handleDelete("t1");
    expect(model.tasks).toHaveLength(1);
    expect(model.total).toBe(1);
  });

  it("handleDelete handles error and shows toast", async () => {
    vi.mocked(api.deleteValidationTask).mockRejectedValue(new Error("Delete failed"));
    await model.handleDelete("t1");
    expect(addToast).toHaveBeenCalledWith("Delete failed", "error");
  });

  it("handleDelete does not mutate if unmounted", async () => {
    vi.mocked(api.deleteValidationTask).mockRejectedValue(new Error("Error"));
    model.destroy();
    await model.handleDelete("t1");
    expect(addToast).not.toHaveBeenCalled();
  });
  // #endregion

  // #region ValidationTasksListModel.HandleToggleStatus [C:2] [TYPE Test]
  it("handleToggleStatus toggles active to inactive", async () => {
    vi.mocked(api.toggleValidationTaskStatus).mockResolvedValue({});
    const task = { id: "t1", is_active: true } as any;
    await model.handleToggleStatus(task);
    expect(api.toggleValidationTaskStatus).toHaveBeenCalledWith("t1", false);
    expect(task.is_active).toBe(false);
  });

  it("handleToggleStatus toggles inactive to active", async () => {
    vi.mocked(api.toggleValidationTaskStatus).mockResolvedValue({});
    const task = { id: "t1", is_active: false } as any;
    await model.handleToggleStatus(task);
    expect(api.toggleValidationTaskStatus).toHaveBeenCalledWith("t1", true);
    expect(task.is_active).toBe(true);
  });

  it("handleToggleStatus handles default is_active (undefined)", async () => {
    vi.mocked(api.toggleValidationTaskStatus).mockResolvedValue({});
    const task = { id: "t1" } as any;
    await model.handleToggleStatus(task);
    expect(api.toggleValidationTaskStatus).toHaveBeenCalledWith("t1", false);
  });

  it("handleToggleStatus handles error and shows toast", async () => {
    vi.mocked(api.toggleValidationTaskStatus).mockRejectedValue(new Error("Toggle failed"));
    await model.handleToggleStatus({ id: "t1", is_active: true } as any);
    expect(addToast).toHaveBeenCalledWith("Toggle failed", "error");
  });

  it("handleToggleStatus returns early if unmounted", async () => {
    vi.mocked(api.toggleValidationTaskStatus).mockRejectedValue(new Error("Error"));
    model.destroy();
    await model.handleToggleStatus({ id: "t1", is_active: true } as any);
    expect(addToast).not.toHaveBeenCalled();
  });
  // #endregion

  // #region ValidationTasksListModel.InitFromLoad [C:2] [TYPE Function]
  describe("ValidationTasksListModel — initFromLoad", () => {
    it("populates state from load data", () => {
      model.initFromLoad({ tasks: [{ id: "t1" }], total: 1, page: 1, page_size: 20, error: null });
      expect(model.tasks).toHaveLength(1);
      expect(model.total).toBe(1);
    });
    it("handles undefined fields", () => {
      model.initFromLoad({});
      expect(model.tasks).toEqual([]);
      expect(model.total).toBe(0);
    });
  });
  // #endregion

  // #region ValidationTasksListModel.HandleRunNowUnmounted [C:2] [TYPE Test]
  it("handleRunNow does not add toast when unmounted on success", async () => {
    vi.mocked(api.triggerValidationRun).mockResolvedValue({});
    model.destroy();
    await model.handleRunNow("t1");
    expect(addToast).not.toHaveBeenCalled();
  });
  // #endregion

  // #region ValidationTasksListModel.HandleDeleteSuccess [C:2] [TYPE Test]
  it("handleDelete clears deleteConfirmId on success", async () => {
    vi.mocked(api.deleteValidationTask).mockResolvedValue({});
    model.deleteConfirmId = "t1";
    model.tasks = [{ id: "t1", name: "Task 1" } as any];
    await model.handleDelete("t1");
    expect(model.deleteConfirmId).toBeNull();
  });
  // #endregion

  // #region ValidationTasksListModel.HandleToggleStatusSuccessCall [C:2] [TYPE Test]
  it("handleToggleStatus updates is_active on task object", async () => {
    vi.mocked(api.toggleValidationTaskStatus).mockResolvedValue({});
    const task = { id: "t1", is_active: false } as any;
    model.tasks = [task];
    await model.handleToggleStatus(task);
    expect(task.is_active).toBe(true);
    expect(model.tasks[0].is_active).toBe(true);
  });
  // #endregion

  // #region ValidationTasksListModel.HandleToggleStatusUnmounted [C:2] [TYPE Test]
  it("handleToggleStatus does not mutate if unmounted on success", async () => {
    vi.mocked(api.toggleValidationTaskStatus).mockResolvedValue({});
    model.destroy();
    const task = { id: "t1", is_active: true } as any;
    await model.handleToggleStatus(task);
    expect(addToast).not.toHaveBeenCalled();
  });
  // #endregion

  // #region ValidationTasksListModel.AdditionalCoverage [C:2] [TYPE Test]
  it("loadTasks returns early when unmounted on success", async () => {
    vi.mocked(api.getValidationTasks).mockResolvedValue({ tasks: [{ id: "t1" }], total: 1 });
    model.destroy();
    await model.loadTasks();
    expect(model.tasks).toEqual([]);
  });

  it("handleSearchInput sets searchQuery and clears existing timeout", () => {
    model.handleSearchInput("first");
    expect(model.searchQuery).toBe("first");
    model.handleSearchInput("second");
    expect(model.searchQuery).toBe("second");
  });

  it("handleDelete returns early when unmounted on success", async () => {
    vi.mocked(api.deleteValidationTask).mockResolvedValue({});
    model.destroy();
    await model.handleDelete("t1");
    expect(addToast).not.toHaveBeenCalled();
  });
  // #endregion
});
// #endregion
