import { describe, expect, it, vi } from "vitest";

import type { AppServerClient } from "../../src/app-server/connection/client";
import { loadHookData } from "../../src/settings/app-server-data";
import {
  createSettingsDynamicSectionLifecycle,
  transitionSettingsDynamicSectionLifecycle,
  transitionSettingsDataRefreshLifecycle,
} from "../../src/settings/lifecycle";

describe("settings lifecycle", () => {
  it("tracks settings data refresh lifecycle", () => {
    const idle = { kind: "idle" } as const;

    const loading = transitionSettingsDataRefreshLifecycle(idle, { type: "started", operationToken: 1 });
    expect(loading).toEqual({ kind: "loading", operationToken: 1 });
    expect(transitionSettingsDataRefreshLifecycle(loading, { type: "started", operationToken: 0 })).toBe(loading);

    const newerLoading = transitionSettingsDataRefreshLifecycle(loading, { type: "started", operationToken: 2 });
    expect(newerLoading).toEqual({ kind: "loading", operationToken: 2 });
    expect(transitionSettingsDataRefreshLifecycle(newerLoading, { type: "completed", failedCount: 2, operationToken: 1 })).toBe(
      newerLoading,
    );

    const completed = transitionSettingsDataRefreshLifecycle(newerLoading, { type: "completed", failedCount: 2, operationToken: 2 });
    expect(completed).toEqual({ kind: "completed", failedCount: 2, operationToken: 2 });
  });

  it("tracks dynamic section lifecycle", () => {
    const idle = createSettingsDynamicSectionLifecycle();
    expect(idle).toEqual({ kind: "idle", status: "" });

    const loading = transitionSettingsDynamicSectionLifecycle(idle, { type: "started", status: "Loading hooks...", operationToken: 1 });
    expect(loading).toEqual({ kind: "loading", status: "Loading hooks...", operationToken: 1 });

    expect(transitionSettingsDynamicSectionLifecycle(loading, { type: "loaded", status: "Stale result.", operationToken: 0 })).toBe(
      loading,
    );

    const loaded = transitionSettingsDynamicSectionLifecycle(loading, { type: "loaded", status: "Loaded 1 hook.", operationToken: 1 });
    expect(loaded).toEqual({ kind: "loaded", status: "Loaded 1 hook.", operationToken: 1 });

    const failed = transitionSettingsDynamicSectionLifecycle(loaded, {
      type: "failed",
      status: "Could not load hooks.",
      operationToken: 1,
    });
    expect(failed).toEqual({ kind: "failed", status: "Could not load hooks.", operationToken: 1 });

    const laterLoaded = transitionSettingsDynamicSectionLifecycle(failed, { type: "loaded", status: "Loaded 2 hooks.", operationToken: 2 });
    expect(
      transitionSettingsDynamicSectionLifecycle(laterLoaded, { type: "started", status: "Loading old hooks...", operationToken: 1 }),
    ).toBe(laterLoaded);
    expect(transitionSettingsDynamicSectionLifecycle(laterLoaded, { type: "failed", status: "Late old failure.", operationToken: 1 })).toBe(
      laterLoaded,
    );

    expect(transitionSettingsDynamicSectionLifecycle(failed, { type: "reset" })).toEqual(idle);
  });

  it("uses only hook rows for the requested cwd", async () => {
    const client = {
      listHooks: vi.fn().mockResolvedValue({
        data: [
          { cwd: "/other", hooks: [{ key: "other" }], warnings: ["skip"], errors: [{ message: "skip" }] },
          { cwd: "/vault", hooks: [{ key: "vault" }], warnings: ["warn"], errors: [{ message: "err" }] },
        ],
      }),
    } as unknown as AppServerClient;

    await expect(loadHookData(client, "/vault")).resolves.toMatchObject({
      hooks: [{ key: "vault" }],
      warnings: ["warn"],
      errors: ['{"message":"err"}'],
      status: "Loaded 1 hook.",
    });
  });

  it("does not fall back to unrelated hook rows", async () => {
    const client = {
      listHooks: vi.fn().mockResolvedValue({
        data: [{ cwd: "/other", hooks: [{ key: "other" }], warnings: ["skip"], errors: [{ message: "skip" }] }],
      }),
    } as unknown as AppServerClient;

    await expect(loadHookData(client, "/vault")).resolves.toMatchObject({
      hooks: [],
      warnings: [],
      errors: [],
      status: "Loaded 0 hooks.",
    });
  });
});
