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

/* ── Mocks ────────────────────────────────────────────────────────── */

const mockSettingsManager = vi.hoisted(() => ({
  DEFAULT_OPENROUTER_MODEL: "openrouter/free",
  getSettings: vi.fn().mockReturnValue({
    openrouterApiKey: "",
    openrouterModel: "",
    linearApiKey: "",
    linearAutoTransition: false,
    linearAutoTransitionStateName: "",
    editorTabEnabled: false,
  }),
  updateSettings: vi.fn(),
}));

const mockLinearCache = vi.hoisted(() => ({
  linearCache: { clear: vi.fn() },
}));

vi.mock("../settings-manager.js", () => mockSettingsManager);
vi.mock("../linear-cache.js", () => mockLinearCache);

/* ── App setup ────────────────────────────────────────────────────── */

import { registerSettingsRoutes } from "./settings-routes.js";

let app: Hono;

beforeEach(() => {
  vi.restoreAllMocks();
  mockSettingsManager.getSettings.mockReturnValue({
    openrouterApiKey: "",
    openrouterModel: "",
    linearApiKey: "",
    linearAutoTransition: false,
    linearAutoTransitionStateName: "",
    editorTabEnabled: false,
  });
  mockSettingsManager.updateSettings.mockReset();
  mockLinearCache.linearCache.clear.mockReset();
  app = new Hono();
  registerSettingsRoutes(app);
});

/* ── Helpers ──────────────────────────────────────────────────────── */

function req(method: string, path: string, body?: unknown) {
  const init: RequestInit = { method, headers: {} };
  if (body) {
    (init.headers as Record<string, string>)["content-type"] = "application/json";
    init.body = JSON.stringify(body);
  }
  return app.request(path, init);
}

/* ── Tests ────────────────────────────────────────────────────────── */

describe("GET /settings", () => {
  it("returns settings with masked keys", async () => {
    mockSettingsManager.getSettings.mockReturnValue({
      openrouterApiKey: "sk-or-123",
      openrouterModel: "gpt-4",
      linearApiKey: "",
      linearAutoTransition: true,
      linearAutoTransitionStateName: "In Progress",
      editorTabEnabled: true,
    });

    const res = await req("GET", "/settings");
    expect(res.status).toBe(200);
    const body = await res.json();
    expect(body.openrouterApiKeyConfigured).toBe(true);
    expect(body.openrouterModel).toBe("gpt-4");
    expect(body.linearApiKeyConfigured).toBe(false);
    expect(body.linearAutoTransition).toBe(true);
    expect(body.editorTabEnabled).toBe(true);
    // Should NOT have the raw key
    expect(body).not.toHaveProperty("openrouterApiKey");
  });

  it("returns default model when empty", async () => {
    const res = await req("GET", "/settings");
    expect(res.status).toBe(200);
    const body = await res.json();
    expect(body.openrouterModel).toBe("openrouter/free");
  });
});

describe("PUT /settings", () => {
  it("updates settings and returns masked result", async () => {
    mockSettingsManager.updateSettings.mockReturnValue({
      openrouterApiKey: "sk-new",
      openrouterModel: "gpt-4",
      linearApiKey: "",
      linearAutoTransition: false,
      linearAutoTransitionStateName: "",
      editorTabEnabled: false,
    });

    const res = await req("PUT", "/settings", {
      openrouterApiKey: "sk-new",
      openrouterModel: "gpt-4",
    });
    expect(res.status).toBe(200);
    const body = await res.json();
    expect(body.openrouterApiKeyConfigured).toBe(true);
    expect(body.openrouterModel).toBe("gpt-4");
  });

  it("clears linear cache when linear API key changes", async () => {
    mockSettingsManager.updateSettings.mockReturnValue({
      openrouterApiKey: "",
      openrouterModel: "",
      linearApiKey: "lin-key",
      linearAutoTransition: false,
      linearAutoTransitionStateName: "",
      editorTabEnabled: false,
    });

    await req("PUT", "/settings", { linearApiKey: "lin-key" });
    expect(mockLinearCache.linearCache.clear).toHaveBeenCalled();
  });

  it("returns 400 when no fields provided", async () => {
    const res = await req("PUT", "/settings", {});
    expect(res.status).toBe(400);
  });

  it("returns 400 on invalid JSON", async () => {
    const res = await app.request("/settings", {
      method: "PUT",
      headers: { "content-type": "application/json" },
      body: "not-json",
    });
    expect(res.status).toBe(400);
  });
});
