import { DEFAULT_OPENAI_MODEL, SystemRole } from "@capakraken/shared";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";

const {
  createAuditEntry,
  testSmtpConnection,
  generateGeminiImage,
  parseGeminiError,
} = vi.hoisted(() => ({
  createAuditEntry: vi.fn(),
  testSmtpConnection: vi.fn(),
  generateGeminiImage: vi.fn(),
  parseGeminiError: vi.fn((error: unknown) => `parsed:${String(error)}`),
}));

vi.mock("../lib/audit.js", () => ({
  createAuditEntry,
}));

vi.mock("../lib/email.js", () => ({
  testSmtpConnection,
}));

vi.mock("../gemini-client.js", () => ({
  generateGeminiImage,
  parseGeminiError,
}));

import {
  clearStoredRuntimeSecrets,
  getAiConfiguredStatus,
  getSystemSettingsView,
  testSettingsGeminiConnection,
  testSettingsSmtpConnection,
  updateSystemSettings,
} from "../router/settings-procedure-support.js";

function createAdminContext(db: Record<string, unknown>) {
  return {
    db: db as never,
    dbUser: {
      id: "admin_1",
      systemRole: SystemRole.ADMIN,
      permissionOverrides: null,
    },
  };
}

describe("settings procedure support", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  afterEach(() => {
    vi.unstubAllEnvs();
  });

  it("builds the system settings view model from db and runtime state", async () => {
    vi.stubEnv("OPENAI_API_KEY", "env-openai-key");
    vi.stubEnv("SMTP_PASSWORD", "env-smtp-password");

    const result = await getSystemSettingsView(createAdminContext({
      systemSettings: {
        findUnique: vi.fn().mockResolvedValue({
          aiProvider: "openai",
          azureOpenAiApiKey: null,
          smtpPassword: null,
        }),
      },
    }));

    expect(result.hasApiKey).toBe(true);
    expect(result.hasSmtpPassword).toBe(true);
    expect(result.aiProvider).toBe("openai");
    expect(result.defaultSummaryPrompt).toBeTypeOf("string");
  });

  it("updates settings without persisting incoming secret fields and writes an audit entry", async () => {
    const findUnique = vi.fn().mockResolvedValue({
      id: "singleton",
      aiProvider: "azure",
      azureOpenAiApiKey: "secret",
    });
    const upsert = vi.fn().mockResolvedValue({});

    const result = await updateSystemSettings(createAdminContext({
      systemSettings: {
        findUnique,
        upsert,
      },
    }), {
      aiProvider: "openai",
      azureOpenAiDeployment: DEFAULT_OPENAI_MODEL,
      azureOpenAiApiKey: "should-be-ignored",
      smtpPassword: "also-ignored",
    });

    expect(result).toEqual({
      ok: true,
      ignoredSecretFields: ["azureOpenAiApiKey", "smtpPassword"],
      secretStorageMode: "environment-only",
    });
    expect(upsert).toHaveBeenCalledWith({
      where: { id: "singleton" },
      create: {
        id: "singleton",
        aiProvider: "openai",
        azureOpenAiDeployment: DEFAULT_OPENAI_MODEL,
      },
      update: {
        aiProvider: "openai",
        azureOpenAiDeployment: DEFAULT_OPENAI_MODEL,
      },
    });
    expect(createAuditEntry).toHaveBeenCalledWith(expect.objectContaining({
      entityType: "SystemSettings",
      action: "UPDATE",
      after: expect.objectContaining({
        aiProvider: "openai",
        azureOpenAiDeployment: DEFAULT_OPENAI_MODEL,
      }),
    }));
  });

  it("clears only legacy runtime secrets that are still stored", async () => {
    const update = vi.fn().mockResolvedValue({});

    const result = await clearStoredRuntimeSecrets(createAdminContext({
      systemSettings: {
        findUnique: vi.fn().mockResolvedValue({
          azureOpenAiApiKey: "db-key",
          azureDalleApiKey: null,
          geminiApiKey: "db-gemini",
          smtpPassword: null,
          anonymizationSeed: "seed",
        }),
        update,
      },
    }));

    expect(result).toEqual({
      ok: true,
      clearedFields: ["azureOpenAiApiKey", "geminiApiKey", "anonymizationSeed"],
    });
    expect(update).toHaveBeenCalledWith({
      where: { id: "singleton" },
      data: {
        azureOpenAiApiKey: null,
        geminiApiKey: null,
        anonymizationSeed: null,
      },
    });
  });

  it("tests smtp and gemini connections with audited outcomes", async () => {
    testSmtpConnection.mockResolvedValue({ ok: true });
    generateGeminiImage.mockResolvedValue("data:image/png;base64,abc123");

    const db = {
      systemSettings: {
        findUnique: vi.fn().mockResolvedValue({
          geminiApiKey: "gem-key",
          geminiModel: "gem-model",
        }),
      },
    };
    const ctx = createAdminContext(db);

    expect(await testSettingsSmtpConnection(ctx)).toEqual({ ok: true });
    expect(await testSettingsGeminiConnection(ctx)).toEqual({
      ok: true,
      model: "gem-model",
      preview: "data:image/png;base64,abc123...",
    });
    expect(createAuditEntry).toHaveBeenCalledTimes(2);
  });

  it("reads the ai configured status from runtime-resolved settings", async () => {
    vi.stubEnv("OPENAI_API_KEY", "env-openai-key");

    const result = await getAiConfiguredStatus(createAdminContext({
      systemSettings: {
        findUnique: vi.fn().mockResolvedValue({
          aiProvider: "openai",
          azureOpenAiDeployment: DEFAULT_OPENAI_MODEL,
          azureOpenAiApiKey: null,
        }),
      },
    }));

    expect(result).toEqual({ configured: true });
  });
});
