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

const mocks = vi.hoisted(() => ({
  hasPotentialConfiguredChannels: vi.fn(),
  resolveCommandConfigWithSecrets: vi.fn(),
  getStatusCommandSecretTargetIds: vi.fn(),
  readBestEffortConfig: vi.fn(),
  resolveOsSummary: vi.fn(),
  createStatusScanCoreBootstrap: vi.fn(),
  callGateway: vi.fn(),
  collectChannelStatusIssues: vi.fn(),
  buildChannelsTable: vi.fn(),
}));

vi.mock("../channels/config-presence.js", () => ({
  hasPotentialConfiguredChannels: mocks.hasPotentialConfiguredChannels,
}));

vi.mock("../cli/command-config-resolution.js", () => ({
  resolveCommandConfigWithSecrets: mocks.resolveCommandConfigWithSecrets,
}));

vi.mock("../cli/command-secret-targets.js", () => ({
  getStatusCommandSecretTargetIds: mocks.getStatusCommandSecretTargetIds,
}));

vi.mock("../config/config.js", () => ({
  readBestEffortConfig: mocks.readBestEffortConfig,
}));

vi.mock("../infra/os-summary.js", () => ({
  resolveOsSummary: mocks.resolveOsSummary,
}));

vi.mock("./status.scan.bootstrap-shared.js", () => ({
  createStatusScanCoreBootstrap: mocks.createStatusScanCoreBootstrap,
}));

vi.mock("../gateway/call.js", () => ({
  callGateway: mocks.callGateway,
}));

vi.mock("./status.scan.runtime.js", () => ({
  statusScanRuntime: {
    collectChannelStatusIssues: mocks.collectChannelStatusIssues,
    buildChannelsTable: mocks.buildChannelsTable,
  },
}));

describe("collectStatusScanOverview", () => {
  beforeEach(() => {
    vi.resetModules();
    vi.clearAllMocks();

    mocks.hasPotentialConfiguredChannels.mockReturnValue(true);
    mocks.getStatusCommandSecretTargetIds.mockReturnValue([]);
    mocks.readBestEffortConfig.mockResolvedValue({ session: {} });
    mocks.resolveCommandConfigWithSecrets.mockResolvedValue({
      resolvedConfig: { session: {} },
      diagnostics: ["secret warning"],
    });
    mocks.resolveOsSummary.mockReturnValue({ label: "test-os" });
    mocks.createStatusScanCoreBootstrap.mockResolvedValue({
      tailscaleMode: "serve",
      tailscaleDnsPromise: Promise.resolve("box.tail.ts.net"),
      updatePromise: Promise.resolve({ installKind: "git" }),
      agentStatusPromise: Promise.resolve({
        defaultId: "main",
        agents: [],
        totalSessions: 0,
        bootstrapPendingCount: 0,
      }),
      gatewayProbePromise: Promise.resolve({
        gatewayConnection: {
          url: "ws://127.0.0.1:18789",
          urlSource: "missing gateway.remote.url (fallback local)",
        },
        remoteUrlMissing: true,
        gatewayMode: "remote",
        gatewayProbeAuth: { token: "tok" },
        gatewayProbeAuthWarning: "warn",
        gatewayProbe: { ok: true, error: null },
        gatewayReachable: true,
        gatewaySelf: { host: "box" },
        gatewayCallOverrides: {
          url: "ws://127.0.0.1:18789",
          token: "tok",
        },
      }),
      resolveTailscaleHttpsUrl: vi.fn(async () => "https://box.tail.ts.net"),
      skipColdStartNetworkChecks: false,
    });
    mocks.callGateway.mockResolvedValue({ channelAccounts: {} });
    mocks.collectChannelStatusIssues.mockReturnValue([{ channel: "signal", message: "boom" }]);
    mocks.buildChannelsTable.mockResolvedValue({ rows: [], details: [] });
  });

  it("uses gateway fallback overrides for channels.status when requested", async () => {
    const { collectStatusScanOverview } = await import("./status.scan-overview.ts");

    const result = await collectStatusScanOverview({
      commandName: "status --all",
      opts: { timeoutMs: 1234 },
      showSecrets: false,
      useGatewayCallOverridesForChannelsStatus: true,
    });

    expect(mocks.callGateway).toHaveBeenCalledWith(
      expect.objectContaining({
        method: "channels.status",
        url: "ws://127.0.0.1:18789",
        token: "tok",
      }),
    );
    expect(mocks.buildChannelsTable).toHaveBeenCalledWith(
      expect.any(Object),
      expect.objectContaining({
        showSecrets: false,
        sourceConfig: { session: {} },
      }),
    );
    expect(result.channelIssues).toEqual([{ channel: "signal", message: "boom" }]);
  });

  it("skips channels.status when the gateway is unreachable", async () => {
    mocks.createStatusScanCoreBootstrap.mockResolvedValueOnce({
      tailscaleMode: "off",
      tailscaleDnsPromise: Promise.resolve(null),
      updatePromise: Promise.resolve({ installKind: "git" }),
      agentStatusPromise: Promise.resolve({
        defaultId: "main",
        agents: [],
        totalSessions: 0,
        bootstrapPendingCount: 0,
      }),
      gatewayProbePromise: Promise.resolve({
        gatewayConnection: {
          url: "ws://127.0.0.1:18789",
          urlSource: "default",
        },
        remoteUrlMissing: false,
        gatewayMode: "local",
        gatewayProbeAuth: {},
        gatewayProbeAuthWarning: undefined,
        gatewayProbe: null,
        gatewayReachable: false,
        gatewaySelf: null,
      }),
      resolveTailscaleHttpsUrl: vi.fn(async () => null),
      skipColdStartNetworkChecks: false,
    });
    const { collectStatusScanOverview } = await import("./status.scan-overview.ts");

    const result = await collectStatusScanOverview({
      commandName: "status",
      opts: {},
      showSecrets: true,
    });

    expect(mocks.callGateway).not.toHaveBeenCalled();
    expect(result.channelsStatus).toBeNull();
    expect(result.channelIssues).toEqual([]);
  });
});
