// #region DatasetDetailModelTests [C:3] [TYPE Module] [SEMANTICS test,model,dataset-detail]
// @BRIEF L1 unit tests for DatasetDetailModel — no DOM render, mocks API + i18n.
// @RELATION BINDS_TO -> [DatasetDetailModel]
// @TEST_INVARIANT: load-gate -> VERIFIED_BY: [test_load_guarded_by_context, test_load_idle_state]
// @TEST_EDGE: missing_field -> TypeError gracefully handled as error
// @TEST_EDGE: invalid_type -> API returns non-object, error state
// @TEST_EDGE: external_fail -> API throws, error state set
import { describe, it, expect, vi, beforeEach } from "vitest";

vi.mock("$app/navigation", () => ({ goto: vi.fn() }));
vi.mock("$lib/api.js", () => ({ api: { getDatasetDetail: vi.fn() }, requestApi: vi.fn() }));
vi.mock("$lib/cot-logger", () => ({ log: vi.fn() }));
vi.mock("$lib/routes.js", () => ({ ROUTES: { datasets: { detail: vi.fn(() => "/datasets/1"), list: vi.fn(() => "/datasets") }, dashboards: { detail: vi.fn(() => "/dashboards/1") } } }));

import { DatasetDetailModel } from "../DatasetDetailModel.svelte.ts";
import { api } from "$lib/api.js";
import { goto } from "$app/navigation";

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

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

  // ═══════════════════════════════════════════════════════════════
  // Initial state
  // ═══════════════════════════════════════════════════════════════
  describe("initial state", () => {
    it("starts in idle state with default values", () => {
      expect(model.screenState).toBe("idle");
      expect(model.dataset).toBeNull();
      expect(model.error).toBeNull();
      expect(model.datasetId).toBe("");
      expect(model.envId).toBe("");
    });

    it("derived isLoading is false when idle", () => {
      expect(model.isLoading).toBe(false);
    });

    it("derived columnCount is 0 when dataset is null", () => {
      expect(model.columnCount).toBe(0);
    });

    it("derived linkedDashCount is 0 when dataset is null", () => {
      expect(model.linkedDashCount).toBe(0);
    });

    it("derived columnCount from dataset data", () => {
      model.dataset = { table_name: "t", database: "d", column_count: 15 };
      expect(model.columnCount).toBe(15);
    });

    it("derived linkedDashCount from dataset data", () => {
      model.dataset = { table_name: "t", database: "d", linked_dashboard_count: 3 };
      expect(model.linkedDashCount).toBe(3);
    });
  });

  // ═══════════════════════════════════════════════════════════════
  // @INVARIANT: loadDatasetDetail() guarded by context
  // ═══════════════════════════════════════════════════════════════
  describe("loadDatasetDetail", () => {
    it("sets error state when datasetId is empty", async () => {
      model.envId = "env-1";
      await model.loadDatasetDetail();
      expect(model.screenState).toBe("error");
      expect(model.error).toContain("Missing context");
    });

    it("sets error state when envId is empty", async () => {
      model.datasetId = "ds-1";
      await model.loadDatasetDetail();
      expect(model.screenState).toBe("error");
      expect(model.error).toContain("Missing context");
    });

    it("loads dataset when datasetId and envId are set", async () => {
      vi.mocked(api.getDatasetDetail).mockResolvedValue({
        table_name: "sales",
        schema: "public",
        database: "prod",
        column_count: 10,
      });
      model.datasetId = "ds-1";
      model.envId = "env-1";

      await model.loadDatasetDetail();

      expect(api.getDatasetDetail).toHaveBeenCalledWith("env-1", "ds-1");
      expect(model.screenState).toBe("loaded");
      expect(model.dataset?.table_name).toBe("sales");
      expect(model.error).toBeNull();
    });

    it("sets loading state before fetch, then loaded after", async () => {
      let resolvePromise!: (v: unknown) => void;
      vi.mocked(api.getDatasetDetail).mockImplementation(() => new Promise((r) => { resolvePromise = r; }));
      model.datasetId = "ds-1";
      model.envId = "env-1";

      const promise = model.loadDatasetDetail();
      expect(model.screenState).toBe("loading");
      expect(model.isLoading).toBe(true);
      resolvePromise({ table_name: "sales" });
      await promise;
      expect(model.screenState).toBe("loaded");
    });

    it("sets error state on API failure", async () => {
      vi.mocked(api.getDatasetDetail).mockRejectedValue(new Error("Network error"));
      model.datasetId = "ds-1";
      model.envId = "env-1";

      await model.loadDatasetDetail();

      expect(model.screenState).toBe("error");
      expect(model.error).toBe("Network error");
    });

    it("handles non-Error rejection gracefully", async () => {
      vi.mocked(api.getDatasetDetail).mockRejectedValue("string error");
      model.datasetId = "ds-1";
      model.envId = "env-1";

      await model.loadDatasetDetail();

      expect(model.screenState).toBe("error");
      expect(model.error).toBeTruthy();
    });
  });

  // ═══════════════════════════════════════════════════════════════
  // Navigation
  // ═══════════════════════════════════════════════════════════════
  describe("navigateToDashboard", () => {
    it("navigates to dashboard detail with slug", () => {
      model.envId = "env-1";
      model.navigateToDashboard({ id: 1, title: "Sales", slug: "sales-dash" });
      expect(goto).toHaveBeenCalled();
    });

    it("navigates to dashboard detail with id when no slug", () => {
      model.envId = "env-1";
      model.navigateToDashboard({ id: "42", title: "Test" });
      expect(goto).toHaveBeenCalled();
    });

    it("does nothing when dashboard ref is falsy", () => {
      model.navigateToDashboard({} as any);
      expect(goto).not.toHaveBeenCalled();
    });
  });

  describe("goBack", () => {
    it("navigates to datasets list", () => {
      model.envId = "env-1";
      model.goBack();
      expect(goto).toHaveBeenCalled();
    });
  });

  // ═══════════════════════════════════════════════════════════════
  // Static utilities — getColumnTypeClass
  // ═══════════════════════════════════════════════════════════════
  describe("getColumnTypeClass (static)", () => {
    it("returns numeric class for int/float/num types", () => {
      expect(DatasetDetailModel.getColumnTypeClass("int")).toContain("text-primary");
      expect(DatasetDetailModel.getColumnTypeClass("integer")).toContain("text-primary");
      expect(DatasetDetailModel.getColumnTypeClass("float")).toContain("text-primary");
      expect(DatasetDetailModel.getColumnTypeClass("number")).toContain("text-primary");
      expect(DatasetDetailModel.getColumnTypeClass("NUMERIC")).toContain("text-primary");
    });

    it("returns date class for date/time types", () => {
      expect(DatasetDetailModel.getColumnTypeClass("date")).toContain("text-success");
      expect(DatasetDetailModel.getColumnTypeClass("datetime")).toContain("text-success");
      expect(DatasetDetailModel.getColumnTypeClass("timestamp")).toContain("text-success");
      expect(DatasetDetailModel.getColumnTypeClass("TIME")).toContain("text-success");
    });

    it("returns string class for str/text/char types", () => {
      expect(DatasetDetailModel.getColumnTypeClass("string")).toContain("text-info");
      expect(DatasetDetailModel.getColumnTypeClass("text")).toContain("text-info");
      expect(DatasetDetailModel.getColumnTypeClass("varchar")).toContain("text-info");
      expect(DatasetDetailModel.getColumnTypeClass("CHAR")).toContain("text-info");
    });

    it("returns bool class for bool types", () => {
      expect(DatasetDetailModel.getColumnTypeClass("bool")).toContain("text-warning");
      expect(DatasetDetailModel.getColumnTypeClass("boolean")).toContain("text-warning");
      expect(DatasetDetailModel.getColumnTypeClass("BOOLEAN")).toContain("text-warning");
    });

    it("returns muted class for unknown types", () => {
      expect(DatasetDetailModel.getColumnTypeClass("json")).toContain("text-text-muted");
      expect(DatasetDetailModel.getColumnTypeClass("uuid")).toContain("bg-surface-muted");
    });

    it("returns muted class for undefined input", () => {
      expect(DatasetDetailModel.getColumnTypeClass(undefined)).toContain("text-text-muted");
    });
  });
});
// #endregion DatasetDetailModelTests
