// #region HealthCenterModelTests [C:3] [TYPE Module] [SEMANTICS test,model,health-center]
// @BRIEF L1 unit tests for HealthCenterModel — no DOM render, mocks API + healthStore + toast.
// @RELATION BINDS_TO -> [HealthCenterModel]
// @TEST_INVARIANT: env-reload -> VERIFIED_BY: [test_env_change_reloads_data]
// @TEST_INVARIANT: matrix-filter-works -> VERIFIED_BY: [test_matrix_select_filters_items]
// @TEST_INVARIANT: delete-tracking -> VERIFIED_BY: [test_delete_adds_to_tracking_set]
import { describe, it, expect, vi, beforeEach } from "vitest";

// ── Mocks ────────────────────────────────────────────────────────

vi.mock("$lib/api.js", () => ({
  getHealthSummary: vi.fn(),
  getEnvironments: vi.fn().mockResolvedValue([]),
  getConsolidatedSettings: vi.fn().mockResolvedValue({ app_timezone: "UTC" }),
  requestApi: vi.fn(),
}));

vi.mock("$lib/stores/health.svelte.js", () => ({
  healthStore: {
    refresh: vi.fn().mockResolvedValue({
      items: [],
      pass_count: 0, warn_count: 0, fail_count: 0, unknown_count: 0,
    }),
  },
}));

vi.mock("$lib/toasts.svelte.js", () => ({ addToast: vi.fn() }));

vi.mock("$lib/cot-logger", () => ({ log: vi.fn() }));

vi.mock("svelte/store", () => ({ get: vi.fn(() => ({ selectedEnvId: "env-1" })) }));
vi.mock("$lib/stores/environmentContext.svelte.js", () => ({
  environmentContextStore: { subscribe: vi.fn() },
}));

import { HealthCenterModel } from "../HealthCenterModel.svelte.ts";
import { getHealthSummary, getEnvironments, getConsolidatedSettings, requestApi } from "$lib/api.js";
import { healthStore } from "$lib/stores/health.svelte.js";
import { addToast } from "$lib/toasts.svelte.js";

// ── Fixtures ─────────────────────────────────────────────────────

function makeHealthSummary(overrides = {}) {
  return {
    items: [
      { dashboard_id: "d1", status: "PASS", environment_id: "env-1", last_check: "2026-06-01T00:00:00Z", record_id: "r1" },
      { dashboard_id: "d2", status: "WARN", environment_id: "env-1", last_check: "2026-06-01T00:00:00Z" },
      { dashboard_id: "d3", status: "FAIL", environment_id: "env-1", last_check: "2026-06-01T00:00:00Z" },
    ],
    pass_count: 1, warn_count: 1, fail_count: 1, unknown_count: 0,
    ...overrides,
  };
}

function makeEnvironments() {
  return [{ id: "env-1", name: "DEV" }, { id: "env-2", name: "PROD" }];
}

// ── Suite ────────────────────────────────────────────────────────

describe("HealthCenterModel — L1 invariants (no render)", () => {
  let model: HealthCenterModel;

  beforeEach(() => {
    vi.clearAllMocks();
    model = new HealthCenterModel();
  });

  // ═══════════════════════════════════════════════════════════════
  // @INVARIANT: Changing environment reloads all data
  // ═══════════════════════════════════════════════════════════════
  describe("handleEnvChange", () => {
    it("sets selectedEnvId, resets filter, and triggers data reload", async () => {
      vi.mocked(getHealthSummary).mockResolvedValue(makeHealthSummary());
      vi.mocked(getEnvironments).mockResolvedValue(makeEnvironments());
      vi.mocked(getConsolidatedSettings).mockResolvedValue({ app_timezone: "UTC" });
      model.activeStatusFilter = "FAIL";
      model.handleEnvChange("env-2");
      expect(model.selectedEnvId).toBe("env-2");
      expect(model.activeStatusFilter).toBe("");
      expect(model.error).toBeNull();
      await vi.waitFor(() => expect(getHealthSummary).toHaveBeenCalledWith("env-2"));
      await vi.waitFor(() => expect(model.screenState).toBe("loaded"));
    });

    it("resets activeStatusFilter on env change", () => {
      model.activeStatusFilter = "FAIL";
      model.handleEnvChange("env-1");
      expect(model.activeStatusFilter).toBe("");
    });
  });

  // ═══════════════════════════════════════════════════════════════
  // @INVARIANT: Matrix click sets activeStatusFilter, re-derived filteredItems
  // ═══════════════════════════════════════════════════════════════
  describe("matrix filter", () => {
    it("sets filter and re-derives filteredItems", () => {
      model.healthData = makeHealthSummary();
      expect(model.filteredItems).toHaveLength(3);
      model.handleMatrixSelect("FAIL");
      expect(model.activeStatusFilter).toBe("FAIL");
      expect(model.filteredItems).toHaveLength(1);
      expect(model.filteredItems[0].dashboard_id).toBe("d3");
    });

    it("clearing filter shows all items", () => {
      model.healthData = makeHealthSummary();
      model.handleMatrixSelect("FAIL");
      expect(model.filteredItems).toHaveLength(1);
      model.clearFilter();
      expect(model.activeStatusFilter).toBe("");
      expect(model.filteredItems).toHaveLength(3);
    });

    it("handleMatrixSelect with empty string shows all items", () => {
      model.healthData = makeHealthSummary();
      model.handleMatrixSelect("FAIL");
      expect(model.filteredItems).toHaveLength(1);
      model.handleMatrixSelect("");
      expect(model.activeStatusFilter).toBe("");
      expect(model.filteredItems).toHaveLength(3);
    });
  });

  // ═══════════════════════════════════════════════════════════════
  // @INVARIANT: Delete tracking via SvelteSet
  // ═══════════════════════════════════════════════════════════════
  describe("handleDeleteReport", () => {
    it("adds record_id to deletingReportIds during deletion", async () => {
      vi.mocked(requestApi).mockResolvedValue({});
      vi.mocked(getHealthSummary).mockResolvedValue(makeHealthSummary());
      vi.mocked(getEnvironments).mockResolvedValue(makeEnvironments());
      vi.mocked(getConsolidatedSettings).mockResolvedValue({ app_timezone: "UTC" });
      const item = { dashboard_id: "d1", environment_id: "env-1", last_check: "", record_id: "r1", status: "PASS" };
      const promise = model.handleDeleteReport(item);
      expect(model.deletingReportIds.has("r1")).toBe(true);
      await promise;
      expect(model.deletingReportIds.has("r1")).toBe(false);
    });

    it("skips delete if record_id is missing", async () => {
      const item = { dashboard_id: "d1", environment_id: "env-1", last_check: "", status: "PASS" };
      await model.handleDeleteReport(item);
      expect(requestApi).not.toHaveBeenCalled();
    });

    it("skips delete if already deleting same record_id", async () => {
      const item = { dashboard_id: "d1", environment_id: "env-1", last_check: "", record_id: "r1", status: "PASS" };
      model.deletingReportIds.add("r1");
      await model.handleDeleteReport(item);
      expect(requestApi).not.toHaveBeenCalled();
    });

    it("reloads data after successful delete", async () => {
      model.selectedEnvId = "env-1";
      vi.mocked(requestApi).mockResolvedValue({});
      vi.mocked(getHealthSummary).mockResolvedValue(makeHealthSummary());
      vi.mocked(getEnvironments).mockResolvedValue(makeEnvironments());
      vi.mocked(getConsolidatedSettings).mockResolvedValue({ app_timezone: "UTC" });
      const item = { dashboard_id: "d1", environment_id: "env-1", last_check: "", record_id: "r1", status: "PASS" };
      await model.handleDeleteReport(item);
      expect(requestApi).toHaveBeenCalledWith("/health/summary/r1", "DELETE");
      await vi.waitFor(() => expect(getHealthSummary).toHaveBeenCalled());
    });

    it("shows toast on delete failure", async () => {
      vi.mocked(requestApi).mockRejectedValue(new Error("Delete API failed"));
      const item = { dashboard_id: "d1", environment_id: "env-1", last_check: "", record_id: "r1", status: "PASS" };
      await model.handleDeleteReport(item);
      expect(addToast).toHaveBeenCalledWith(expect.stringContaining("Delete API failed"), "error");
    });
  });

  // ═══════════════════════════════════════════════════════════════
  // loadData lifecycle
  // ═══════════════════════════════════════════════════════════════
  describe("loadData", () => {
    it("sets screenState to loaded on success with env selected", async () => {
      model.selectedEnvId = "env-1";
      vi.mocked(getHealthSummary).mockResolvedValue(makeHealthSummary());
      vi.mocked(getEnvironments).mockResolvedValue(makeEnvironments());
      vi.mocked(getConsolidatedSettings).mockResolvedValue({ app_timezone: "UTC" });
      await model.loadData();
      expect(model.screenState).toBe("loaded");
      expect(model.healthData.items).toHaveLength(3);
      expect(model.environments).toHaveLength(2);
    });

    it("loads via healthStore.refresh when no env selected", async () => {
      vi.mocked(getEnvironments).mockResolvedValue(makeEnvironments());
      vi.mocked(getConsolidatedSettings).mockResolvedValue({ app_timezone: "UTC" });
      await model.loadData();
      expect(model.screenState).toBe("loaded");
      expect(model.healthData).toBeDefined();
    });

    it("sets screenState to error when healthSummary fails with env selected", async () => {
      model.selectedEnvId = "env-1";
      vi.mocked(getHealthSummary).mockRejectedValue(new Error("API down"));
      vi.mocked(getEnvironments).mockResolvedValue([]);
      vi.mocked(getConsolidatedSettings).mockResolvedValue({ app_timezone: "UTC" });
      await model.loadData();
      expect(model.screenState).toBe("error");
      expect(model.error).toBe("API down");
    });

    it("catches error from env fetch", async () => {
      model.selectedEnvId = "env-1";
      vi.mocked(getHealthSummary).mockResolvedValue(makeHealthSummary());
      vi.mocked(getEnvironments).mockRejectedValue(new Error("Envs failed"));
      vi.mocked(getConsolidatedSettings).mockResolvedValue({ app_timezone: "UTC" });
      await model.loadData();
      expect(model.screenState).toBe("error");
      expect(model.error).toBe("Envs failed");
    });

    it("inits with loading state", () => {
      expect(model.screenState).toBe("loading");
      expect(model.deletingReportIds.size).toBe(0);
    });

    it("uses fallback timezone when getConsolidatedSettings fails", async () => {
      model.selectedEnvId = "env-1";
      vi.mocked(getHealthSummary).mockResolvedValue(makeHealthSummary());
      vi.mocked(getEnvironments).mockResolvedValue(makeEnvironments());
      vi.mocked(getConsolidatedSettings).mockRejectedValue(new Error("Settings down"));
      await model.loadData();
      expect(model.appTimezone).toBe("Europe/Moscow");
    });
  });

  describe("handleEnvChange triggers reload", () => {
    it("sets loading state and calls loadData", async () => {
      const loadSpy = vi.spyOn(model, 'loadData').mockResolvedValue();
      model.handleEnvChange('env-1');
      expect(model.selectedEnvId).toBe('env-1');
      expect(loadSpy).toHaveBeenCalled();
      loadSpy.mockRestore();
    });
  });

  describe("clearFilter", () => {
    it("resets activeStatusFilter to empty string", () => {
      model.activeStatusFilter = "FAIL";
      model.clearFilter();
      expect(model.activeStatusFilter).toBe("");
    });
  });

  describe("env restore from context store", () => {
    it("restores selectedEnvId from environment context when not set", async () => {
      vi.mocked(getHealthSummary).mockResolvedValue(makeHealthSummary());
      vi.mocked(getEnvironments).mockResolvedValue(makeEnvironments());
      vi.mocked(getConsolidatedSettings).mockResolvedValue({ app_timezone: "UTC" });
      model.selectedEnvId = "";
      await model.loadData();
      expect(model.selectedEnvId).toBe("env-1");
    });
  });
});
