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

import { buildPayloadForSection } from "@/composables/configPayload";
import type { AdminConfigForm, AdminConfigSnapshot } from "@/types/admin-config";

function createSnapshot(): AdminConfigSnapshot {
  return {
    default_provider: "openai",
    default_model: "gpt-4.1",
    api_url: "http://localhost:3000/api",
    default_temperature: 0.7,
    memory_backend: "sqlite",
    observability_backend: "none",
    otel_endpoint: "",
    otel_service_name: "",
    runtime_kind: "native",
    autonomy_level: "supervised",
    autonomy_workspace_only: true,
    autonomy_max_actions_per_hour: 20,
    autonomy_max_cost_per_day_cents: 500,
    autonomy_require_approval_for_medium_risk: true,
    autonomy_block_high_risk_commands: true,
    autonomy_auto_approve: "",
    autonomy_always_ask: "",
    identity_format: "openclaw",
    identity_aieos_path: "",
    scheduler_enabled: true,
    scheduler_max_tasks: 64,
    scheduler_max_concurrent: 4,
    gateway_port: 3000,
    gateway_host: "127.0.0.1",
    gateway_require_pairing: true,
    gateway_allow_public_bind: false,
    gateway_pair_rate_limit_per_minute: 10,
    gateway_webhook_rate_limit_per_minute: 60,
    webhook_enabled: false,
    webhook_port: 3001,
    webhook_secret_exists: false,
    web_search_enabled: false,
    web_search_provider: "duckduckgo",
    web_search_max_results: 5,
    web_search_timeout_secs: 10,
    web_search_has_brave_api_key: false,
    browser_has_computer_use_api_key: false,
    composio_enabled: false,
    composio_entity_id: "default",
    composio_has_api_key: false,
    memory_cerebro_endpoint: "",
    memory_cerebro_timeout_ms: 5000,
    memory_cerebro_allow_insecure_loopback: false,
    memory_cerebro_has_auth_token: false,
  };
}

function createForm(overrides: Partial<AdminConfigForm> = {}): AdminConfigForm {
  const snapshot = createSnapshot();
  return {
    default_provider: snapshot.default_provider,
    default_model: snapshot.default_model,
    api_url: snapshot.api_url,
    default_temperature: `${snapshot.default_temperature}`,
    memory_backend: snapshot.memory_backend,
    observability_backend: snapshot.observability_backend,
    otel_endpoint: snapshot.otel_endpoint,
    otel_service_name: snapshot.otel_service_name,
    runtime_kind: snapshot.runtime_kind,
    autonomy_level: snapshot.autonomy_level,
    autonomy_workspace_only: snapshot.autonomy_workspace_only,
    autonomy_max_actions_per_hour: `${snapshot.autonomy_max_actions_per_hour}`,
    autonomy_max_cost_per_day_cents: `${snapshot.autonomy_max_cost_per_day_cents}`,
    autonomy_require_approval_for_medium_risk: snapshot.autonomy_require_approval_for_medium_risk,
    autonomy_block_high_risk_commands: snapshot.autonomy_block_high_risk_commands,
    autonomy_auto_approve: snapshot.autonomy_auto_approve,
    autonomy_always_ask: snapshot.autonomy_always_ask,
    identity_format: snapshot.identity_format,
    identity_aieos_path: snapshot.identity_aieos_path,
    scheduler_enabled: snapshot.scheduler_enabled,
    scheduler_max_tasks: `${snapshot.scheduler_max_tasks}`,
    scheduler_max_concurrent: `${snapshot.scheduler_max_concurrent}`,
    gateway_port: `${snapshot.gateway_port}`,
    gateway_host: snapshot.gateway_host,
    gateway_require_pairing: snapshot.gateway_require_pairing,
    gateway_allow_public_bind: snapshot.gateway_allow_public_bind,
    gateway_pair_rate_limit_per_minute: `${snapshot.gateway_pair_rate_limit_per_minute}`,
    gateway_webhook_rate_limit_per_minute: `${snapshot.gateway_webhook_rate_limit_per_minute}`,
    webhook_enabled: snapshot.webhook_enabled,
    webhook_port: `${snapshot.webhook_port}`,
    webhook_secret_mode: "unchanged",
    webhook_secret_value: "",
    webhook_secret_exists: snapshot.webhook_secret_exists,
    web_search_enabled: snapshot.web_search_enabled,
    web_search_provider: snapshot.web_search_provider,
    web_search_max_results: `${snapshot.web_search_max_results}`,
    web_search_timeout_secs: `${snapshot.web_search_timeout_secs}`,
    web_search_brave_api_key_mode: "unchanged",
    web_search_brave_api_key_value: "",
    web_search_has_brave_api_key: snapshot.web_search_has_brave_api_key,
    browser_computer_use_api_key_mode: "unchanged",
    browser_computer_use_api_key_value: "",
    browser_has_computer_use_api_key: snapshot.browser_has_computer_use_api_key,
    composio_enabled: snapshot.composio_enabled,
    composio_entity_id: snapshot.composio_entity_id,
    composio_api_key_mode: "unchanged",
    composio_api_key_value: "",
    composio_has_api_key: snapshot.composio_has_api_key,
    memory_cerebro_endpoint: snapshot.memory_cerebro_endpoint,
    memory_cerebro_timeout_ms: `${snapshot.memory_cerebro_timeout_ms}`,
    memory_cerebro_allow_insecure_loopback: snapshot.memory_cerebro_allow_insecure_loopback,
    memory_cerebro_auth_token_mode: "unchanged",
    memory_cerebro_auth_token_value: "",
    memory_cerebro_has_auth_token: snapshot.memory_cerebro_has_auth_token,
    ...overrides,
  };
}

describe("buildPayloadForSection", () => {
  it("builds diff-only payloads for general settings", () => {
    const snapshot = createSnapshot();
    const payload = buildPayloadForSection(
      "general",
      createForm({
        default_provider: "anthropic",
        default_temperature: "not-a-number",
        memory_backend: "surreal",
      }),
      snapshot
    );

    expect(payload).toEqual({
      default_provider: "anthropic",
      memory_backend: "surreal",
    });
  });

  it("nests changed security fields and ignores unchanged values", () => {
    const snapshot = createSnapshot();
    const payload = buildPayloadForSection(
      "security",
      createForm({
        autonomy_level: "full",
        autonomy_workspace_only: false,
        autonomy_max_actions_per_hour: "30",
        autonomy_max_cost_per_day_cents: "",
        identity_aieos_path: "/tmp/identity.json",
      }),
      snapshot
    );

    expect(payload).toEqual({
      autonomy: {
        level: "full",
        workspace_only: false,
        max_actions_per_hour: 30,
      },
      identity: {
        aieos_path: "/tmp/identity.json",
      },
    });
  });

  it("builds observability, runtime, scheduler, and gateway sections only when changed", () => {
    const snapshot = createSnapshot();

    expect(
      buildPayloadForSection(
        "observability",
        createForm({
          observability_backend: "otel",
          otel_endpoint: "http://localhost:4318",
          otel_service_name: "dashboard",
        }),
        snapshot
      )
    ).toEqual({
      observability: {
        backend: "otel",
        otel_endpoint: "http://localhost:4318",
        otel_service_name: "dashboard",
      },
    });

    expect(buildPayloadForSection("runtime", createForm(), snapshot)).toEqual({});
    expect(
      buildPayloadForSection("runtime", createForm({ runtime_kind: "docker" }), snapshot)
    ).toEqual({ runtime: { kind: "docker" } });

    expect(
      buildPayloadForSection(
        "scheduler",
        createForm({ scheduler_enabled: false, scheduler_max_tasks: "96" }),
        snapshot
      )
    ).toEqual({
      scheduler: {
        enabled: false,
        max_tasks: 96,
      },
    });

    expect(
      buildPayloadForSection(
        "gateway",
        createForm({
          gateway_port: "4000",
          gateway_allow_public_bind: true,
          gateway_pair_rate_limit_per_minute: "",
        }),
        snapshot
      )
    ).toEqual({
      gateway: {
        port: 4000,
        allow_public_bind: true,
      },
    });
  });

  it("supports webhook secret clear and replace flows", () => {
    const snapshot = createSnapshot();

    expect(
      buildPayloadForSection(
        "webhook",
        createForm({ webhook_enabled: true, webhook_secret_mode: "clear" }),
        snapshot
      )
    ).toEqual({
      channels: {
        webhook: {
          enabled: true,
          secret: { mode: "clear" },
        },
      },
    });

    expect(
      buildPayloadForSection(
        "webhook",
        createForm({
          webhook_secret_mode: "replace",
          webhook_secret_value: "  top-secret  ",
        }),
        snapshot
      )
    ).toEqual({
      channels: {
        webhook: {
          secret: { mode: "replace", value: "top-secret" },
        },
      },
    });
  });

  it("throws when replacing webhook secret with an empty value", () => {
    expect(() =>
      buildPayloadForSection(
        "webhook",
        createForm({ webhook_secret_mode: "replace", webhook_secret_value: "   " }),
        createSnapshot()
      )
    ).toThrowError("empty_webhook_secret");
  });

  it("builds web-search payload with changed fields and brave api key replace", () => {
    const snapshot = createSnapshot();
    const payload = buildPayloadForSection(
      "web-search",
      createForm({
        web_search_enabled: true,
        web_search_provider: "brave",
        web_search_max_results: "10",
        web_search_brave_api_key_mode: "replace",
        web_search_brave_api_key_value: "  brave-key-123  ",
      }),
      snapshot
    );

    expect(payload).toEqual({
      web_search: {
        enabled: true,
        provider: "brave",
        max_results: 10,
        brave_api_key: { mode: "replace", value: "brave-key-123" },
      },
    });
  });

  it("builds web-search payload with brave api key clear", () => {
    const snapshot = createSnapshot();
    const payload = buildPayloadForSection(
      "web-search",
      createForm({
        web_search_brave_api_key_mode: "clear",
      }),
      snapshot
    );

    expect(payload).toEqual({
      web_search: {
        brave_api_key: { mode: "clear" },
      },
    });
  });

  it("returns empty payload for web-search when nothing changed", () => {
    const snapshot = createSnapshot();
    expect(buildPayloadForSection("web-search", createForm(), snapshot)).toEqual({});
  });

  it("builds browser payload with computer_use_api_key replace", () => {
    const payload = buildPayloadForSection(
      "browser",
      createForm({
        browser_computer_use_api_key_mode: "replace",
        browser_computer_use_api_key_value: "  cu-key  ",
      }),
      createSnapshot()
    );

    expect(payload).toEqual({
      browser: {
        computer_use_api_key: { mode: "replace", value: "cu-key" },
      },
    });
  });

  it("builds browser payload with computer_use_api_key clear", () => {
    const payload = buildPayloadForSection(
      "browser",
      createForm({
        browser_computer_use_api_key_mode: "clear",
      }),
      createSnapshot()
    );

    expect(payload).toEqual({
      browser: {
        computer_use_api_key: { mode: "clear" },
      },
    });
  });

  it("returns empty payload for browser when mode is unchanged", () => {
    expect(buildPayloadForSection("browser", createForm(), createSnapshot())).toEqual({});
  });

  it("throws for browser when replacing with empty value", () => {
    expect(() =>
      buildPayloadForSection(
        "browser",
        createForm({
          browser_computer_use_api_key_mode: "replace",
          browser_computer_use_api_key_value: "   ",
        }),
        createSnapshot()
      )
    ).toThrowError("empty_secret");
  });

  it("builds composio payload with changed fields and api key replace", () => {
    const snapshot = createSnapshot();
    const payload = buildPayloadForSection(
      "composio",
      createForm({
        composio_enabled: true,
        composio_entity_id: "custom",
        composio_api_key_mode: "replace",
        composio_api_key_value: "  composio-key  ",
      }),
      snapshot
    );

    expect(payload).toEqual({
      composio: {
        enabled: true,
        entity_id: "custom",
        api_key: { mode: "replace", value: "composio-key" },
      },
    });
  });

  it("builds composio payload with api key clear", () => {
    const payload = buildPayloadForSection(
      "composio",
      createForm({ composio_api_key_mode: "clear" }),
      createSnapshot()
    );

    expect(payload).toEqual({
      composio: {
        api_key: { mode: "clear" },
      },
    });
  });

  it("returns empty payload for composio when nothing changed", () => {
    expect(buildPayloadForSection("composio", createForm(), createSnapshot())).toEqual({});
  });

  it("builds memory payload with changed cerebro fields and auth token replace", () => {
    const snapshot = createSnapshot();
    const payload = buildPayloadForSection(
      "memory",
      createForm({
        memory_cerebro_endpoint: "http://cerebro:9090",
        memory_cerebro_timeout_ms: "10000",
        memory_cerebro_auth_token_mode: "replace",
        memory_cerebro_auth_token_value: "  token-abc  ",
      }),
      snapshot
    );

    expect(payload).toEqual({
      memory: {
        cerebro: {
          endpoint: "http://cerebro:9090",
          request_timeout_ms: 10000,
          auth_token: { mode: "replace", value: "token-abc" },
        },
      },
    });
  });

  it("builds memory payload with auth token clear", () => {
    const payload = buildPayloadForSection(
      "memory",
      createForm({ memory_cerebro_auth_token_mode: "clear" }),
      createSnapshot()
    );

    expect(payload).toEqual({
      memory: {
        cerebro: {
          auth_token: { mode: "clear" },
        },
      },
    });
  });

  it("returns empty payload for memory when nothing changed", () => {
    expect(buildPayloadForSection("memory", createForm(), createSnapshot())).toEqual({});
  });

  it("throws empty_secret for web-search when replacing with empty value", () => {
    expect(() =>
      buildPayloadForSection(
        "web-search",
        createForm({
          web_search_brave_api_key_mode: "replace",
          web_search_brave_api_key_value: "   ",
        }),
        createSnapshot()
      )
    ).toThrowError("empty_secret");
  });

  it("throws empty_secret for composio when replacing with empty value", () => {
    expect(() =>
      buildPayloadForSection(
        "composio",
        createForm({
          composio_api_key_mode: "replace",
          composio_api_key_value: "   ",
        }),
        createSnapshot()
      )
    ).toThrowError("empty_secret");
  });

  it("throws empty_secret for memory when replacing auth token with empty value", () => {
    expect(() =>
      buildPayloadForSection(
        "memory",
        createForm({
          memory_cerebro_auth_token_mode: "replace",
          memory_cerebro_auth_token_value: "   ",
        }),
        createSnapshot()
      )
    ).toThrowError("empty_secret");
  });

  it("builds security payload with flipped boolean flags", () => {
    const snapshot = createSnapshot();
    const payload = buildPayloadForSection(
      "security",
      createForm({
        autonomy_require_approval_for_medium_risk: false,
        autonomy_block_high_risk_commands: false,
      }),
      snapshot
    );

    expect(payload).toEqual({
      autonomy: {
        require_approval_for_medium_risk: false,
        block_high_risk_commands: false,
      },
    });
  });

  it("builds security payload with auto_approve and always_ask list changes", () => {
    const snapshot = createSnapshot();
    const payload = buildPayloadForSection(
      "security",
      createForm({
        autonomy_auto_approve: "file_read, file_write",
        autonomy_always_ask: "shell_exec",
      }),
      snapshot
    );

    expect(payload).toEqual({
      autonomy: {
        auto_approve: ["file_read", "file_write"],
        always_ask: ["shell_exec"],
      },
    });
  });

  it("returns empty for provider-pools and updates sections", () => {
    const snapshot = createSnapshot();
    expect(buildPayloadForSection("provider-pools", createForm(), snapshot)).toEqual({});
    expect(buildPayloadForSection("updates", createForm(), snapshot)).toEqual({});
  });
});
