import OpenAI from 'openai';
import { z } from 'zod';
import { getSystemPromptSuggest } from '@/lib/db/repositories';
import { DEFAULT_SYSTEM_PROMPT_SUGGEST } from '@/lib/db/defaults';
import { getOpenRouterKey, getOpenRouterKeyOptional } from "@/core/settings";

// Expliziter Pin auf Sonnet 4.6 (1M Context, 64k Output, $3/$15 in/out per M-Token).
// Vorher '~anthropic/claude-sonnet-latest' — der Tilde-Pointer routet bei neuen Releases automatisch um.
const DEFAULT_MODEL = 'anthropic/claude-sonnet-4.6';
const MAX_TOKENS = 2000;

// Schema bewusst sehr lenient — KI generiert oft längere Snippets als geplant,
// und Längen-Caps sind im UI eh nicht hart durchgesetzt. Lieber den Suggest durchlaufen lassen
// als 422 mitten im UX-Flow.
const SuggestionSchema = z.object({
  campaignName: z.string().min(3).max(120).optional(),
  offer: z.string().min(5).max(800).optional(),
  painPoint: z.string().min(5).max(800).optional(),
  ctaPrimaryText: z.string().min(2).max(80).optional(),
  heroBriefHint: z.string().max(800).optional(),
  inferred: z.array(z.string()).optional().describe('Liste der Feldnamen die als plausible Vermutung (nicht direkt im Brief) gefüllt wurden'),
  reasoning: z.string().optional().describe('Kurzer Hinweis welche Felder NICHT füllbar waren oder welche Annahmen gemacht wurden'),
});

export type BriefingSuggestion = z.infer<typeof SuggestionSchema>;

// System-Prompt wird zur Laufzeit aus app_settings.system_prompt_suggest geladen
// (Fallback: DEFAULT_SYSTEM_PROMPT_SUGGEST in src/lib/db/defaults.ts).
// Siehe Verwendung in der completion.create() unten.

const SUGGEST_TOOL: OpenAI.Chat.Completions.ChatCompletionTool = {
  type: 'function',
  function: {
    name: 'suggest_briefing',
    description: 'Liefert strukturierte Briefing-Vorschläge aus dem User-Brief.',
    parameters: {
      type: 'object',
      properties: {
        campaignName: { type: 'string', description: 'Kurzer interner Kampagnen-Name (3-80 Zeichen)' },
        offer: { type: 'string', description: 'Was wird beworben? Konkretes Angebot.' },
        painPoint: { type: 'string', description: 'Schmerzpunkt der Zielgruppe' },
        ctaPrimaryText: { type: 'string', description: 'Button-Text für primären CTA' },
        heroBriefHint: { type: 'string', description: 'Bildkompositions-Hinweis für Hero-Bild' },
        inferred: { type: 'array', items: { type: 'string' }, description: 'Feldnamen die als plausible Vermutung gefüllt wurden (nicht direkt aus Brief)' },
        reasoning: { type: 'string', description: 'Kurzer Hinweis was NICHT aus dem Brief ableitbar war' },
      },
    },
  },
};

export interface SuggestInput {
  briefText: string;
  briefHtml?: string;
  existing: Record<string, unknown>; // bereits gefüllte Felder, damit das LLM diese nicht überschreibt
}

export interface SuggestResult {
  suggestions: BriefingSuggestion;
  modelUsed: string;
  tokensIn?: number;
  tokensOut?: number;
  durationMs: number;
}

export async function suggestBriefing(input: SuggestInput): Promise<SuggestResult> {
  const start = Date.now();

  if (!getOpenRouterKeyOptional()) {
    return {
      suggestions: produceStubSuggestions(input.briefText),
      modelUsed: 'stub-no-api-key',
      durationMs: Date.now() - start,
    };
  }

  const model = process.env.OPENROUTER_MODEL ?? DEFAULT_MODEL;
  const client = new OpenAI({
    baseURL: 'https://openrouter.ai/api/v1',
    apiKey: getOpenRouterKey(),
    defaultHeaders: {
      'HTTP-Referer': process.env.OPENROUTER_APP_URL ?? 'http://localhost:3000',
      'X-Title': process.env.OPENROUTER_APP_TITLE ?? 'MailForge',
    },
  });

  const filledKeys = Object.entries(input.existing)
    .filter(([, v]) => v !== undefined && v !== null && v !== '')
    .map(([k]) => k);

  const userMsg = [
    `# User-Brief`,
    input.briefText.trim() || '(leer)',
    ``,
    filledKeys.length > 0 ? `# Bereits gefüllte Felder (NICHT überschreiben)\n${filledKeys.join(', ')}` : '',
    ``,
    `Extrahiere Briefing-Felder via suggest_briefing-Tool. Nur Felder vorschlagen, die klar aus dem Brief ableitbar sind.`,
  ]
    .filter(Boolean)
    .join('\n');

  const completion = await client.chat.completions.create({
    model,
    max_tokens: MAX_TOKENS,
    messages: [
      { role: 'system', content: getSystemPromptSuggest() || DEFAULT_SYSTEM_PROMPT_SUGGEST },
      { role: 'user', content: userMsg },
    ],
    tools: [SUGGEST_TOOL],
    tool_choice: { type: 'function', function: { name: 'suggest_briefing' } },
  });

  const toolCall = completion.choices[0]?.message?.tool_calls?.[0];
  if (!toolCall || toolCall.type !== 'function') {
    throw new Error('Model returned no suggest_briefing tool call');
  }

  const args = JSON.parse(toolCall.function.arguments);
  const parsed = SuggestionSchema.safeParse(args);
  if (!parsed.success) {
    throw new Error(`Invalid suggestion schema: ${parsed.error.issues.slice(0, 2).map((i) => i.message).join('; ')}`);
  }

  // Filter out fields the user already has — defensive, in addition to system-prompt instruction
  const filtered = { ...parsed.data };
  for (const k of filledKeys) {
    if (k in filtered) delete (filtered as Record<string, unknown>)[k];
  }

  return {
    suggestions: filtered,
    modelUsed: model,
    tokensIn: completion.usage?.prompt_tokens,
    tokensOut: completion.usage?.completion_tokens,
    durationMs: Date.now() - start,
  };
}

function produceStubSuggestions(text: string): BriefingSuggestion {
  const out: BriefingSuggestion = {
    reasoning: '[Stub] Setze OPENROUTER_API_KEY für echte AI-Vorschläge',
  };
  const firstLine = text.split('\n').map((l) => l.trim()).find((l) => l.length > 0);
  if (firstLine && firstLine.length < 80) out.campaignName = firstLine;
  return out;
}
