import { z } from "zod";
import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc.js";
import { createAiClient, isAiConfigured, parseAiError } from "../ai-client.js";
import { DEFAULT_SUMMARY_PROMPT } from "./resource.js";
import { VALUE_SCORE_WEIGHTS } from "@capakraken/shared";
import { testSmtpConnection } from "../lib/email.js";
import { createAuditEntry } from "../lib/audit.js";

/** Fields that must never appear in audit log values */
const SENSITIVE_FIELDS = new Set([
  "azureOpenAiApiKey",
  "smtpPassword",
  "azureDalleApiKey",
  "anonymizationSeed",
  "geminiApiKey",
]);

export const settingsRouter = createTRPCRouter({
  getSystemSettings: adminProcedure.query(async ({ ctx }) => {
    const settings = await ctx.db.systemSettings.findUnique({
      where: { id: "singleton" },
    });

    const defaultWeights = {
      skillDepth: VALUE_SCORE_WEIGHTS.SKILL_DEPTH,
      skillBreadth: VALUE_SCORE_WEIGHTS.SKILL_BREADTH,
      costEfficiency: VALUE_SCORE_WEIGHTS.COST_EFFICIENCY,
      chargeability: VALUE_SCORE_WEIGHTS.CHARGEABILITY,
      experience: VALUE_SCORE_WEIGHTS.EXPERIENCE,
    };

    return {
      aiProvider: settings?.aiProvider ?? "openai",
      azureOpenAiEndpoint: settings?.azureOpenAiEndpoint ?? null,
      azureOpenAiDeployment: settings?.azureOpenAiDeployment ?? null,
      azureApiVersion: settings?.azureApiVersion ?? "2025-01-01-preview",
      aiMaxCompletionTokens: settings?.aiMaxCompletionTokens ?? 300,
      aiTemperature: settings?.aiTemperature ?? 1,
      aiSummaryPrompt: settings?.aiSummaryPrompt ?? null,
      defaultSummaryPrompt: DEFAULT_SUMMARY_PROMPT,
      hasApiKey: !!settings?.azureOpenAiApiKey,
      scoreWeights: (settings?.scoreWeights as unknown as typeof defaultWeights) ?? defaultWeights,
      scoreVisibleRoles: (settings?.scoreVisibleRoles as unknown as string[]) ?? ["ADMIN", "MANAGER"],
      // SMTP
      smtpHost: settings?.smtpHost ?? null,
      smtpPort: settings?.smtpPort ?? 587,
      smtpUser: settings?.smtpUser ?? null,
      smtpFrom: settings?.smtpFrom ?? null,
      smtpTls: settings?.smtpTls ?? true,
      hasSmtpPassword: !!settings?.smtpPassword,
      // Global anonymization
      anonymizationEnabled: settings?.anonymizationEnabled ?? false,
      anonymizationDomain: settings?.anonymizationDomain ?? "superhartmut.de",
      anonymizationMode: settings?.anonymizationMode ?? "global",
      // DALL-E
      azureDalleDeployment: settings?.azureDalleDeployment ?? null,
      azureDalleEndpoint: settings?.azureDalleEndpoint ?? null,
      hasDalleApiKey: !!settings?.azureDalleApiKey,
      // Gemini
      geminiModel: settings?.geminiModel ?? "gemini-2.5-flash-image",
      hasGeminiApiKey: !!settings?.geminiApiKey,
      // Image provider
      imageProvider: settings?.imageProvider ?? "dalle",
      // Vacation defaults
      vacationDefaultDays: settings?.vacationDefaultDays ?? 28,
      // Timeline
      timelineUndoMaxSteps: settings?.timelineUndoMaxSteps ?? 50,
    };
  }),

  updateSystemSettings: adminProcedure
    .input(
      z.object({
        aiProvider: z.enum(["openai", "azure"]).optional(),
        azureOpenAiEndpoint: z.string().url().optional().or(z.literal("")),
        azureOpenAiDeployment: z.string().optional(),
        azureOpenAiApiKey: z.string().optional(),
        azureApiVersion: z.string().optional(),
        aiMaxCompletionTokens: z.number().int().min(50).max(4000).optional(),
        aiTemperature: z.number().min(0).max(2).optional(),
        aiSummaryPrompt: z.string().optional(),
        scoreWeights: z.object({
          skillDepth: z.number().min(0).max(1),
          skillBreadth: z.number().min(0).max(1),
          costEfficiency: z.number().min(0).max(1),
          chargeability: z.number().min(0).max(1),
          experience: z.number().min(0).max(1),
        }).refine(
          (w) => {
            const sum = w.skillDepth + w.skillBreadth + w.costEfficiency + w.chargeability + w.experience;
            return Math.abs(sum - 1.0) < 0.01;
          },
          { message: "Score weights must sum to 1.0" },
        ).optional(),
        scoreVisibleRoles: z.array(z.enum(["ADMIN", "MANAGER", "CONTROLLER", "USER", "VIEWER"])).optional(),
        // SMTP
        smtpHost: z.string().optional(),
        smtpPort: z.number().int().min(1).max(65535).optional(),
        smtpUser: z.string().optional(),
        smtpPassword: z.string().optional(),
        smtpFrom: z.string().email().optional().or(z.literal("")),
        smtpTls: z.boolean().optional(),
        // Global anonymization
        anonymizationEnabled: z.boolean().optional(),
        anonymizationDomain: z.string().trim().min(1).optional(),
        anonymizationSeed: z.string().trim().min(1).optional().or(z.literal("")),
        anonymizationMode: z.enum(["global"]).optional(),
        // DALL-E image generation
        azureDalleDeployment: z.string().optional(),
        azureDalleEndpoint: z.string().url().optional().or(z.literal("")),
        azureDalleApiKey: z.string().optional(),
        // Gemini image generation
        geminiApiKey: z.string().optional(),
        geminiModel: z.string().optional(),
        // Image provider selection
        imageProvider: z.enum(["dalle", "gemini"]).optional(),
        // Vacation
        vacationDefaultDays: z.number().int().min(0).max(365).optional(),
        // Timeline
        timelineUndoMaxSteps: z.number().int().min(1).max(200).optional(),
      }),
    )
    .mutation(async ({ ctx, input }) => {
      const data: Record<string, unknown> = {};
      if (input.aiProvider !== undefined) data.aiProvider = input.aiProvider;
      if (input.azureOpenAiEndpoint !== undefined)
        data.azureOpenAiEndpoint = input.azureOpenAiEndpoint || null;
      if (input.azureOpenAiDeployment !== undefined)
        data.azureOpenAiDeployment = input.azureOpenAiDeployment || null;
      if (input.azureOpenAiApiKey !== undefined)
        data.azureOpenAiApiKey = input.azureOpenAiApiKey || null;
      if (input.azureApiVersion !== undefined)
        data.azureApiVersion = input.azureApiVersion || null;
      if (input.aiMaxCompletionTokens !== undefined)
        data.aiMaxCompletionTokens = input.aiMaxCompletionTokens;
      if (input.aiTemperature !== undefined)
        data.aiTemperature = input.aiTemperature;
      if (input.aiSummaryPrompt !== undefined)
        data.aiSummaryPrompt = input.aiSummaryPrompt || null;
      if (input.scoreWeights !== undefined)
        data.scoreWeights = input.scoreWeights;
      if (input.scoreVisibleRoles !== undefined)
        data.scoreVisibleRoles = input.scoreVisibleRoles;
      // SMTP
      if (input.smtpHost !== undefined) data.smtpHost = input.smtpHost || null;
      if (input.smtpPort !== undefined) data.smtpPort = input.smtpPort;
      if (input.smtpUser !== undefined) data.smtpUser = input.smtpUser || null;
      if (input.smtpPassword !== undefined) data.smtpPassword = input.smtpPassword || null;
      if (input.smtpFrom !== undefined) data.smtpFrom = input.smtpFrom || null;
      if (input.smtpTls !== undefined) data.smtpTls = input.smtpTls;
      // Global anonymization
      if (input.anonymizationEnabled !== undefined) data.anonymizationEnabled = input.anonymizationEnabled;
      if (input.anonymizationDomain !== undefined) data.anonymizationDomain = input.anonymizationDomain || "superhartmut.de";
      if (input.anonymizationSeed !== undefined) {
        data.anonymizationSeed = input.anonymizationSeed || null;
        data.anonymizationAliases = null;
      }
      if (input.anonymizationMode !== undefined) {
        data.anonymizationMode = input.anonymizationMode;
        data.anonymizationAliases = null;
      }
      // DALL-E
      if (input.azureDalleDeployment !== undefined)
        data.azureDalleDeployment = input.azureDalleDeployment || null;
      if (input.azureDalleEndpoint !== undefined)
        data.azureDalleEndpoint = input.azureDalleEndpoint || null;
      if (input.azureDalleApiKey !== undefined)
        data.azureDalleApiKey = input.azureDalleApiKey || null;
      // Gemini
      if (input.geminiApiKey !== undefined)
        data.geminiApiKey = input.geminiApiKey || null;
      if (input.geminiModel !== undefined)
        data.geminiModel = input.geminiModel || null;
      // Image provider
      if (input.imageProvider !== undefined)
        data.imageProvider = input.imageProvider;
      // Vacation
      if (input.vacationDefaultDays !== undefined) data.vacationDefaultDays = input.vacationDefaultDays;
      // Timeline
      if (input.timelineUndoMaxSteps !== undefined) data.timelineUndoMaxSteps = input.timelineUndoMaxSteps;

      // Fetch current settings for before-snapshot
      const before = await ctx.db.systemSettings.findUnique({ where: { id: "singleton" } });

      await ctx.db.systemSettings.upsert({
        where: { id: "singleton" },
        create: { id: "singleton", ...data },
        update: data,
      });

      // Build sanitized snapshots — redact sensitive fields
      const sanitize = (obj: Record<string, unknown>): Record<string, unknown> => {
        const result: Record<string, unknown> = {};
        for (const [key, value] of Object.entries(obj)) {
          result[key] = SENSITIVE_FIELDS.has(key) ? (value ? "***" : null) : value;
        }
        return result;
      };

      const sanitizedBefore = before ? sanitize(before as unknown as Record<string, unknown>) : undefined;
      const sanitizedAfter = sanitize(data);

      void createAuditEntry({
        db: ctx.db,
        entityType: "SystemSettings",
        entityId: "singleton",
        entityName: "System Settings",
        action: before ? "UPDATE" : "CREATE",
        userId: ctx.dbUser?.id,
        ...(sanitizedBefore !== undefined ? { before: sanitizedBefore } : {}),
        after: sanitizedAfter,
        source: "ui",
      });

      return { ok: true };
    }),

  testAiConnection: adminProcedure.mutation(async ({ ctx }) => {
    const settings = await ctx.db.systemSettings.findUnique({
      where: { id: "singleton" },
    });

    if (!isAiConfigured(settings)) {
      const provider = settings?.aiProvider ?? "openai";
      if (provider === "azure") {
        return { ok: false, error: "Missing required fields: endpoint, deployment name, and API key are all required for Azure OpenAI." };
      }
      return { ok: false, error: "Missing required fields: model name and API key are required." };
    }

    const provider = settings!.aiProvider ?? "openai";
    const apiKey = settings!.azureOpenAiApiKey!;

    let url: string;
    let headers: Record<string, string>;

    if (provider === "azure") {
      const endpoint = settings!.azureOpenAiEndpoint!.replace(/\/$/, "");
      const deployment = settings!.azureOpenAiDeployment!;
      const apiVersion = settings!.azureApiVersion ?? "2025-01-01-preview";
      url = `${endpoint}/openai/deployments/${deployment}/chat/completions?api-version=${apiVersion}`;
      headers = { "Content-Type": "application/json", "api-key": apiKey };
    } else {
      // Standard OpenAI API — deployment field holds the model name (e.g. "gpt-4o")
      const model = settings!.azureOpenAiDeployment ?? "gpt-4o-mini";
      url = "https://api.openai.com/v1/chat/completions";
      headers = { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` };
      // Override body to include model field for OpenAI
      try {
        const resp = await fetch(url, {
          method: "POST",
          headers,
          body: JSON.stringify({
            model,
            messages: [{ role: "user", content: "ping" }],
            max_completion_tokens: 5,
          }),
        });
        const body = await resp.text();
        if (resp.ok) return { ok: true, raw: null };
        let msg = body;
        try {
          const parsed = JSON.parse(body) as { error?: { message?: string } };
          if (parsed.error?.message) msg = parsed.error.message;
        } catch { /* keep raw */ }
        const raw = `HTTP ${resp.status}: ${msg}`;
        return { ok: false, error: parseAiError(new Error(raw)), raw };
      } catch (err) {
        const raw = err instanceof Error ? err.message : String(err);
        return { ok: false, error: parseAiError(err), raw };
      }
    }

    try {
      const resp = await fetch(url, {
        method: "POST",
        headers,
        body: JSON.stringify({
          messages: [{ role: "user", content: "ping" }],
          max_completion_tokens: 5,
        }),
      });

      const body = await resp.text();

      if (resp.ok) {
        return { ok: true, raw: null };
      }

      let azureMessage = body;
      try {
        const parsed = JSON.parse(body) as { error?: { message?: string; code?: string } };
        if (parsed.error?.message) azureMessage = parsed.error.message;
      } catch { /* leave as raw text */ }

      const raw = `HTTP ${resp.status}: ${azureMessage}`;
      return { ok: false, error: parseAiError(new Error(raw)), raw };
    } catch (err) {
      const raw = err instanceof Error ? err.message : String(err);
      return { ok: false, error: parseAiError(err), raw };
    }
  }),

  testSmtpConnection: adminProcedure.mutation(async ({ ctx }) => {
    const result = await testSmtpConnection();

    void createAuditEntry({
      db: ctx.db,
      entityType: "SystemSettings",
      entityId: "singleton",
      entityName: "SMTP Connection Test",
      action: "UPDATE",
      userId: ctx.dbUser?.id,
      after: { testResult: result.ok ? "success" : "failed" },
      source: "ui",
      summary: result.ok ? "SMTP connection test succeeded" : "SMTP connection test failed",
    });

    return result;
  }),

  testGeminiConnection: adminProcedure.mutation(async ({ ctx }) => {
    const settings = await ctx.db.systemSettings.findUnique({
      where: { id: "singleton" },
      select: { geminiApiKey: true, geminiModel: true },
    });

    if (!settings?.geminiApiKey) {
      return { ok: false, error: "Gemini API key is not configured." };
    }

    try {
      const { generateGeminiImage } = await import("../gemini-client.js");
      const model = settings.geminiModel ?? "gemini-2.5-flash-image";

      // Generate a tiny test image with a simple prompt
      const dataUrl = await generateGeminiImage(
        settings.geminiApiKey,
        "A simple blue circle on white background, minimal, 256x256",
        model,
      );

      const hasImage = dataUrl.startsWith("data:image/");

      void createAuditEntry({
        db: ctx.db,
        entityType: "SystemSettings",
        entityId: "singleton",
        entityName: "Gemini Connection Test",
        action: "UPDATE",
        userId: ctx.dbUser?.id,
        after: { testResult: hasImage ? "success" : "failed" },
        source: "ui",
        summary: hasImage ? "Gemini image generation test succeeded" : "Gemini test returned no image",
      });

      return { ok: hasImage, model, preview: hasImage ? dataUrl.slice(0, 100) + "..." : undefined };
    } catch (err) {
      const { parseGeminiError } = await import("../gemini-client.js");
      return { ok: false, error: parseGeminiError(err) };
    }
  }),

  getAiConfigured: protectedProcedure.query(async ({ ctx }) => {
    const settings = await ctx.db.systemSettings.findUnique({
      where: { id: "singleton" },
      select: {
        aiProvider: true,
        azureOpenAiEndpoint: true,
        azureOpenAiDeployment: true,
        azureOpenAiApiKey: true,
      },
    });
    return { configured: isAiConfigured(settings) };
  }),
});
