import { useCallback, useEffect, useRef, useState } from 'react';
import { storage } from '../../lib/storage';
import { callLLM } from '../../lib/llm';
import * as db from '../../lib/db';
import { buildKbContext, validateDelta, deltaKey } from './rag';
import {
  buildSystemPrompt,
  PROPOSE_GRAPH_DELTA_TOOL,
  LOOKUP_TOPIC_TOOL,
  STRINGS,
} from './prompts';

const MAX_HISTORY = 50;
const VERBATIM_TURNS = 12;
const MAX_LOOKUP_HOPS = 3;
const TRUNCATION_NOTICE = '(earlier conversation truncated)';

/** Trim API history to the last N turns and prepend a truncation notice. */
function truncateApiMessages(history) {
  if (history.length <= VERBATIM_TURNS) return history;
  const tail = history.slice(-VERBATIM_TURNS);
  return [{ role: 'assistant', content: TRUNCATION_NOTICE }, ...tail];
}

async function resolveLookupTopic(id, allTopics) {
  const topic = allTopics.find(t => t.id === id);
  if (!topic) {
    return { ok: false, payload: `Geen topic gevonden met id "${id}".` };
  }
  const content = await db.getContent(id).catch(() => null);
  let contentSnippet = null;
  if (content) {
    let raw;
    if (typeof content === 'string') raw = content;
    else if (content.article) raw = typeof content.article === 'string' ? content.article : JSON.stringify(content.article);
    else raw = JSON.stringify(content);
    contentSnippet = raw.replace(/\s+/g, ' ').trim().slice(0, 2400);
  }
  return {
    ok: true,
    payload: {
      id: topic.id,
      label: topic.label,
      type: topic.type || 'concept',
      description: topic.description || '',
      learning_content: contentSnippet,
    },
  };
}

/**
 * Conversation hook for R42.
 * Owns the message list, persists to chat:thread:{userId}, calls Anthropic
 * via the shared `callLLM` client (with a `lookup_topic` multi-hop loop and
 * the `propose_graph_delta` tool), and surfaces validated graph delta
 * suggestions inline.
 */
export function useChat({ user, isAdmin }) {
  const threadKey = user ? `chat:thread:${user.id}` : null;
  const [messages, setMessages] = useState([]);
  const [isThinking, setIsThinking] = useState(false);
  const [errored, setErrored] = useState(false);
  const seenDeltaKeys = useRef(new Set());

  useEffect(() => {
    if (!user) return;
    const stored = storage.get(threadKey, null);
    if (stored && Array.isArray(stored) && stored.length) {
      setMessages(stored);
      for (const m of stored) {
        if (m.suggestion?.key) seenDeltaKeys.current.add(m.suggestion.key);
      }
    } else {
      setMessages([
        {
          id: `m_${Date.now()}`,
          role: 'assistant',
          content: STRINGS.greeting(user.name || 'daar'),
          ts: Date.now(),
        },
      ]);
    }
  }, [user, threadKey]);

  useEffect(() => {
    if (!threadKey) return;
    const capped = messages.slice(-MAX_HISTORY);
    storage.set(threadKey, capped);
  }, [messages, threadKey]);

  const updateMessage = useCallback((id, patch) => {
    setMessages(prev => prev.map(m => (m.id === id ? { ...m, ...patch } : m)));
  }, []);

  /** Wipe the persisted thread and reset to a fresh greeting. */
  const clearThread = useCallback(() => {
    seenDeltaKeys.current = new Set();
    const greeting = {
      id: `m_${Date.now()}`,
      role: 'assistant',
      content: STRINGS.greeting(user?.name || 'daar'),
      ts: Date.now(),
    };
    setMessages([greeting]);
    if (threadKey) storage.set(threadKey, [greeting]);
  }, [user, threadKey]);

  const send = useCallback(async (text) => {
    const trimmed = (text || '').trim();
    if (!trimmed || !user) return;

    const userMsg = {
      id: `m_${Date.now()}_u`,
      role: 'user',
      content: trimmed,
      ts: Date.now(),
    };
    const next = [...messages, userMsg];
    setMessages(next);
    setIsThinking(true);
    setErrored(false);

    try {
      const { context: kbContext, allTopics } = await buildKbContext(trimmed);
      const systemPrompt = buildSystemPrompt({
        userName: user.name || 'daar',
        isAdmin,
        kbContext,
      });

      const historyMessages = next
        .filter(m => m.role === 'user' || m.role === 'assistant')
        .map(m => ({ role: m.role, content: m.content }));

      const apiMessages = truncateApiMessages(historyMessages);

      let textOut = '';
      let suggestion = null;
      let hops = 0;

      while (true) {
        const response = await callLLM({
          task: 'chat.r42',
          tier: 'standard',
          system: systemPrompt,
          messages: apiMessages,
          tools: [LOOKUP_TOPIC_TOOL, PROPOSE_GRAPH_DELTA_TOOL],
          maxTokens: 2048,
          temperature: 0.3,
        });

        if (response.text) {
          textOut += (textOut ? '\n' : '') + response.text;
        }

        const lookupCalls = response.toolUses.filter(tu => tu.name === LOOKUP_TOPIC_TOOL.name);
        const deltaCall = response.toolUses.find(tu => tu.name === PROPOSE_GRAPH_DELTA_TOOL.name);

        if (deltaCall && !suggestion) {
          const validated = validateDelta(deltaCall.input, allTopics);
          if (validated) {
            const key = deltaKey(validated);
            if (!seenDeltaKeys.current.has(key)) {
              seenDeltaKeys.current.add(key);
              suggestion = { ...validated, key, status: 'pending' };
            }
          }
        }

        if (lookupCalls.length === 0 || hops >= MAX_LOOKUP_HOPS) break;
        hops++;

        const assistantBlocks = [];
        if (response.text) assistantBlocks.push({ type: 'text', text: response.text });
        for (const tu of response.toolUses) {
          if (!tu.id) continue;
          assistantBlocks.push({ type: 'tool_use', id: tu.id, name: tu.name, input: tu.input });
        }
        apiMessages.push({ role: 'assistant', content: assistantBlocks });

        const toolResults = [];
        for (const tu of lookupCalls) {
          if (!tu.id) continue;
          const topicId = String(tu.input?.id || '').trim();
          const resolved = await resolveLookupTopic(topicId, allTopics);
          toolResults.push({
            type: 'tool_result',
            tool_use_id: tu.id,
            content: typeof resolved.payload === 'string'
              ? resolved.payload
              : JSON.stringify(resolved.payload),
            ...(resolved.ok ? {} : { is_error: true }),
          });
        }
        apiMessages.push({ role: 'user', content: toolResults });
      }

      const assistantMsg = {
        id: `m_${Date.now()}_a`,
        role: 'assistant',
        content: textOut || (suggestion ? STRINGS.suggestionTitle : STRINGS.errorGeneric),
        ts: Date.now(),
        ...(suggestion ? { suggestion } : {}),
      };
      setMessages(prev => [...prev, assistantMsg]);
    } catch (e) {
      console.error('[R42] chat error', e);
      setErrored(true);
      
      let errorContent = STRINGS.errorGeneric;
      const errorMsg = e?.message || '';
      
      if (e?.name === 'LLMTruncatedError') {
        errorContent = 'Mijn circuits zijn overbelast (Token Limiet bereikt). Kun je je vraag korter of specifieker maken?';
      } else if (e?.name === 'LLMValidationError') {
        errorContent = 'Mijn antwoord was helaas beschadigd of incorrect geformatteerd. Kun je het nog eens proberen?';
      } else if (/api key/i.test(errorMsg)) {
        errorContent = STRINGS.errorNoKey;
      }

      setMessages(prev => [
        ...prev,
        {
          id: `m_${Date.now()}_e`,
          role: 'error',
          content: errorContent,
          ts: Date.now(),
        },
      ]);
    } finally {
      setIsThinking(false);
    }
  }, [messages, user, isAdmin]);

  return {
    messages,
    isThinking,
    errored,
    send,
    updateMessage,
    clearThread,
  };
}
