/**
 * useCommcoach Hook
 * 
 * State management for CommCoach coaching sessions, contexts, and chat.
 */

import { useState, useEffect, useCallback, useRef, type MutableRefObject } from 'react';
import { useApiRequest } from './useApi';
import { useInstanceId } from './useCurrentInstance';
import {
  getContextsApi, createContextApi, getContextDetailApi,
  startSessionStreamApi, completeSessionApi, cancelSessionApi,
  sendMessageStreamApi, sendAudioStreamApi,
  createTaskApi, updateTaskStatusApi, deleteTaskApi,
  type CoachingContext, type CoachingSession, type CoachingMessage,
  type CoachingTask, type CoachingScore, type SSEEvent,
  type SendMessageOptions,
} from '../api/commcoachApi';
import { useTtsPlayback, type TtsEvent } from './useTtsPlayback';

export interface CommcoachHookReturn {
  contexts: CoachingContext[];
  selectedContextId: string | null;
  selectedContext: CoachingContext | null;
  loadingContexts: boolean;

  session: CoachingSession | null;
  messages: CoachingMessage[];
  isStreaming: boolean;
  streamingStatus: string | null;
  streamingMessage: string | null;

  tasks: CoachingTask[];
  scores: CoachingScore[];
  sessions: CoachingSession[];

  error: string | null;
  inputValue: string;
  setInputValue: (v: string) => void;

  agentToolCalls: Array<{ toolName: string; args?: Record<string, unknown>; result?: string; success?: boolean }>;

  selectContext: (contextId: string, options?: { skipSessionResume?: boolean }) => Promise<void>;
  createContext: (title: string, description?: string, category?: string, goals?: string[]) => Promise<void>;
  archiveContext: (contextId: string) => Promise<void>;

  startSession: (personaId?: string) => Promise<void>;
  sendMessage: (content: string, options?: SendMessageOptions) => Promise<void>;
  sendAudio: (audioBlob: Blob) => Promise<void>;
  completeSession: () => Promise<void>;
  cancelSession: () => Promise<void>;

  stopTts: () => void;
  pauseTts: () => void;
  resumeTts: () => void;
  hasAudioToResume: () => boolean;
  ttsIsPlaying: boolean;
  ttsIsPaused: boolean;

  onTtsEventRef: MutableRefObject<((event: TtsEvent) => void) | null>;

  actionLoading: string | null;

  toggleTaskStatus: (taskId: string, currentStatus: string) => Promise<void>;
  addTask: (title: string, description?: string) => Promise<void>;
  removeTask: (taskId: string) => Promise<void>;

  onDocumentCreatedRef: MutableRefObject<((doc: any) => void) | null>;

  refreshContexts: () => Promise<void>;
}

export function useCommcoach(instanceIdOverride?: string): CommcoachHookReturn {
  const { request } = useApiRequest();
  const routeInstanceId = useInstanceId();
  const instanceId = instanceIdOverride || routeInstanceId;

  const [contexts, setContexts] = useState<CoachingContext[]>([]);
  const [selectedContextId, setSelectedContextId] = useState<string | null>(null);
  const [selectedContext, setSelectedContext] = useState<CoachingContext | null>(null);
  const [loadingContexts, setLoadingContexts] = useState(false);

  const [session, setSession] = useState<CoachingSession | null>(null);
  const [messages, setMessages] = useState<CoachingMessage[]>([]);
  const [isStreaming, setIsStreaming] = useState(false);
  const [streamingStatus, setStreamingStatus] = useState<string | null>(null);
  const [streamingMessage, setStreamingMessage] = useState<string | null>(null);

  const [tasks, setTasks] = useState<CoachingTask[]>([]);
  const [scores, setScores] = useState<CoachingScore[]>([]);
  const [sessions, setSessions] = useState<CoachingSession[]>([]);

  const [error, setError] = useState<string | null>(null);
  const [inputValue, setInputValue] = useState('');
  const [agentToolCalls, setAgentToolCalls] = useState<Array<{ toolName: string; args?: Record<string, unknown>; result?: string; success?: boolean }>>([]);

  const [actionLoading, setActionLoading] = useState<string | null>(null);

  const isMountedRef = useRef(true);
  const abortControllerRef = useRef<AbortController | null>(null);
  const onTtsEventRef = useRef<((event: TtsEvent) => void) | null>(null);
  const onDocumentCreatedRef = useRef<((doc: any) => void) | null>(null);

  const ttsPlayback = useTtsPlayback({
    onPlaying: () => { (window as any).__dlog?.('TTS-PLAYING'); onTtsEventRef.current?.('playing'); },
    onEnded: () => { (window as any).__dlog?.('TTS-ENDED'); onTtsEventRef.current?.('ended'); },
    onPaused: () => { (window as any).__dlog?.('TTS-PAUSED'); onTtsEventRef.current?.('paused'); },
    onError: () => { (window as any).__dlog?.('TTS-ERROR'); onTtsEventRef.current?.('error'); },
  });

  useEffect(() => {
    isMountedRef.current = true;
    return () => { isMountedRef.current = false; };
  }, []);

  const refreshContexts = useCallback(async () => {
    if (!instanceId) return;
    setLoadingContexts(true);
    setError(null);
    try {
      const data = await getContextsApi(request, instanceId);
      if (isMountedRef.current) setContexts(data);
    } catch (err: any) {
      if (isMountedRef.current) setError(err.message || 'Fehler beim Laden der Kontexte');
    } finally {
      if (isMountedRef.current) setLoadingContexts(false);
    }
  }, [request, instanceId]);

  const stopTts = useCallback(() => {
    ttsPlayback.stop();
  }, [ttsPlayback]);

  const pauseTts = useCallback(() => {
    ttsPlayback.pause();
  }, [ttsPlayback]);

  const resumeTts = useCallback(() => {
    ttsPlayback.resume();
  }, [ttsPlayback]);

  const hasAudioToResume = useCallback(() => {
    return ttsPlayback.isPaused;
  }, [ttsPlayback]);

  const selectContext = useCallback(async (contextId: string, options?: { skipSessionResume?: boolean }) => {
    if (!instanceId) return;
    setSelectedContextId(contextId);
    setError(null);
    try {
      const detail = await getContextDetailApi(request, instanceId, contextId);
      if (!isMountedRef.current) return;
      setSelectedContext(detail.context);
      setTasks(detail.tasks || []);
      setScores(detail.scores || []);
      setSessions(detail.sessions || []);

      if (options?.skipSessionResume) {
        setSession(null);
        setMessages([]);
        return;
      }

      const activeSession = detail.sessions?.find((s: CoachingSession) => s.status === 'active');
      if (activeSession) {
        await _unlockAudioForTts();
        setIsStreaming(true);
        await startSessionStreamApi(
          instanceId,
          contextId,
          (event: SSEEvent) => {
            if (!isMountedRef.current) return;
            const eventType = event.type;
            const eventData = event.data;
            if (eventType === 'sessionState' && eventData) {
              const sess = eventData.session;
              if (sess) setSession(sess);
              if (eventData.resumed && Array.isArray(eventData.messages)) {
                setMessages(eventData.messages);
              }
            } else if (eventType === 'ttsAudio' && eventData?.audio) {
              ttsPlayback.play(eventData.audio);
            }
            if (eventType === 'complete') setIsStreaming(false);
          },
          (err) => { if (isMountedRef.current) { setError(err.message); setIsStreaming(false); } },
          () => { if (isMountedRef.current) setIsStreaming(false); },
        );
      } else {
        setSession(null);
        setMessages([]);
      }
    } catch (err: any) {
      if (isMountedRef.current) setError(err.message || 'Fehler beim Laden des Kontexts');
    }
  }, [request, instanceId, ttsPlayback.play]);

  const createContext = useCallback(async (title: string, description?: string, category?: string, goals?: string[]) => {
    if (!instanceId) return;
    setActionLoading('creating');
    try {
      const created = await createContextApi(request, instanceId, { title, description, category, goals });
      if (isMountedRef.current) {
        await refreshContexts();
        setSelectedContextId(created.id);
        setSelectedContext(created);
        setTasks([]);
        setScores([]);
        setSessions([]);
        setSession(null);
        setMessages([]);
      }
    } catch (err: any) {
      if (isMountedRef.current) setError(err.message || 'Fehler beim Erstellen des Kontexts');
    } finally {
      if (isMountedRef.current) setActionLoading(null);
    }
  }, [request, instanceId, refreshContexts]);

  const archiveContext = useCallback(async (contextId: string) => {
    if (!instanceId) return;
    setActionLoading('archiving');
    try {
      const { archiveContextApi } = await import('../api/commcoachApi');
      await archiveContextApi(request, instanceId, contextId);
      if (isMountedRef.current) {
        await refreshContexts();
        if (selectedContextId === contextId) {
          setSelectedContextId(null);
          setSelectedContext(null);
        }
      }
    } catch (err: any) {
      if (isMountedRef.current) setError(err.message || 'Fehler beim Archivieren');
    } finally {
      if (isMountedRef.current) setActionLoading(null);
    }
  }, [request, instanceId, selectedContextId, refreshContexts]);

  const startSessionCb = useCallback(async (personaId?: string) => {
    if (!instanceId || !selectedContextId) return;
    setActionLoading('starting');
    await _unlockAudioForTts();
    setError(null);
    setIsStreaming(true);
    setStreamingStatus(null);
    setStreamingMessage(null);
    setMessages([]);
    setSession(null);
    try {
      await startSessionStreamApi(
        instanceId,
        selectedContextId,
        (event: SSEEvent) => {
          if (!isMountedRef.current) return;
          const eventType = event.type;
          const eventData = event.data;

          if (eventType === 'sessionState' && eventData) {
            const sess = eventData.session;
            if (sess) {
              setSession(sess);
            }
            if (eventData.resumed && Array.isArray(eventData.messages)) {
              setMessages(eventData.messages);
            }
          } else if (eventType === 'messageChunk' && eventData) {
            setStreamingStatus(prev => prev || 'Coach formuliert Antwort...');
          } else if (eventType === 'message' && eventData) {
            setStreamingMessage(null);
            const msg: CoachingMessage = {
              id: eventData.id || `msg-${Date.now()}`,
              sessionId: eventData.sessionId || '',
              contextId: eventData.contextId || '',
              role: eventData.role,
              content: eventData.content,
              contentType: eventData.contentType || 'text',
              createdAt: eventData.createdAt,
            };
            setMessages(prev => {
              if (prev.some(m => m.id === msg.id)) return prev;
              return [...prev, msg];
            });
          } else if (eventType === 'ttsAudio' && eventData?.audio) {
            ttsPlayback.play(eventData.audio);
          } else if (eventType === 'status' && eventData) {
            setStreamingStatus(eventData.label || null);
          } else if (eventType === 'taskCreated' && eventData) {
            setTasks(prev => [eventData, ...prev]);
          } else if (eventType === 'documentCreated' && eventData) {
            onDocumentCreatedRef.current?.(eventData);
          } else if (eventType === 'error' && eventData) {
            setError(eventData.message || 'Stream-Fehler');
          }
        },
        (err) => {
          if (isMountedRef.current) {
            setError(err.message);
            setIsStreaming(false);
            setStreamingMessage(null);
          }
        },
        () => {
          if (isMountedRef.current) {
            setIsStreaming(false);
            setStreamingStatus(null);
            setStreamingMessage(null);
          }
        },
        personaId,
      );
    } catch (err: any) {
      if (isMountedRef.current) {
        setError(err.message || 'Fehler beim Starten der Session');
        setIsStreaming(false);
      }
    } finally {
      if (isMountedRef.current) setActionLoading(null);
    }
  }, [instanceId, selectedContextId, ttsPlayback.play]);

  const sendMessage = useCallback(async (content: string, options?: SendMessageOptions) => {
    const normalizedContent = content.trim();
    if (!normalizedContent || !instanceId || !session) return;

    abortControllerRef.current?.abort();
    const ac = new AbortController();
    abortControllerRef.current = ac;

    ttsPlayback.stop();
    await _unlockAudioForTts();
    setError(null);
    setIsStreaming(true);
    setStreamingStatus(null);
    setStreamingMessage(null);
    setAgentToolCalls([]);

    const tempMsg: CoachingMessage = {
      id: `temp-${Date.now()}`,
      sessionId: session.id,
      contextId: session.contextId,
      role: 'user',
      content: normalizedContent,
      contentType: 'text',
      createdAt: new Date().toISOString(),
    };
    setMessages(prev => [...prev, tempMsg]);
    setInputValue('');

    try {
      await sendMessageStreamApi(
        instanceId,
        session.id,
        normalizedContent,
        (event: SSEEvent) => {
          if (!isMountedRef.current || ac.signal.aborted) return;
          const eventType = event.type;
          const eventData = event.data;

          if (eventType === 'messageChunk' && eventData) {
            setStreamingStatus(prev => prev || 'Coach formuliert Antwort...');
          } else if (eventType === 'message' && eventData) {
            setStreamingMessage(null);
            const msg: CoachingMessage = {
              id: eventData.id || `msg-${Date.now()}`,
              sessionId: session.id,
              contextId: session.contextId,
              role: eventData.role,
              content: eventData.content,
              contentType: 'text',
              createdAt: eventData.createdAt,
            };
            setMessages(prev => {
              if (msg.role === 'user') {
                return prev.map(m => m.id === tempMsg.id ? msg : m);
              }
              if (prev.some(m => m.id === msg.id)) return prev;
              return [...prev, msg];
            });
          } else if (eventType === 'ttsAudio' && eventData?.audio) {
            setError(null);
            ttsPlayback.play(eventData.audio);
          } else if (eventType === 'status' && eventData) {
            setStreamingStatus(eventData.label || null);
          } else if (eventType === 'toolCall' && eventData) {
            setAgentToolCalls(prev => [...prev, { toolName: eventData.toolName, args: eventData.args }]);
            setStreamingStatus(`Tool: ${eventData.toolName}...`);
          } else if (eventType === 'toolResult' && eventData) {
            setAgentToolCalls(prev => prev.map((tc, idx) =>
              idx === prev.length - 1
                ? { ...tc, result: eventData.data?.slice(0, 200), success: eventData.success }
                : tc
            ));
          } else if (eventType === 'agentProgress' && eventData) {
            setStreamingStatus(`Runde ${eventData.round}/${eventData.maxRounds}...`);
          } else if (eventType === 'taskCreated' && eventData) {
            setTasks(prev => [eventData, ...prev]);
          } else if (eventType === 'documentCreated' && eventData) {
            onDocumentCreatedRef.current?.(eventData);
          } else if (eventType === 'scoreUpdate') {
            // Will refresh on complete
          } else if (eventType === 'error' && eventData) {
            setError(eventData.message || 'Stream-Fehler');
          }
        },
        (err) => {
          if (err.name === 'AbortError') return;
          if (isMountedRef.current) {
            setError(err.message);
            setIsStreaming(false);
            setStreamingMessage(null);
          }
        },
        () => {
          if (isMountedRef.current) {
            setIsStreaming(false);
            setStreamingStatus(null);
            setStreamingMessage(null);
          }
        },
        ac.signal,
        options,
      );
    } catch (err: any) {
      if (err.name === 'AbortError') return;
      if (isMountedRef.current) {
        setError(err.message);
        setIsStreaming(false);
      }
    }
  }, [instanceId, session, ttsPlayback.play]);

  const sendAudio = useCallback(async (audioBlob: Blob) => {
    if (!instanceId || !session) return;
    ttsPlayback.stop();
    await _unlockAudioForTts();
    setError(null);
    setIsStreaming(true);
    setStreamingStatus(null);
    setStreamingMessage(null);
    try {
      await sendAudioStreamApi(
        instanceId,
        session.id,
        audioBlob,
        (event: SSEEvent) => {
          if (!isMountedRef.current) return;
          const eventType = event.type;
          const eventData = event.data;

          if (eventType === 'messageChunk' && eventData) {
            setStreamingStatus(prev => prev || 'Coach formuliert Antwort...');
          } else if (eventType === 'status' && eventData) {
            setStreamingStatus(eventData.label || null);
          } else if (eventType === 'message' && eventData) {
            if (eventData.role === 'assistant') setError(null);
            const msg: CoachingMessage = {
              id: eventData.id || `msg-${Date.now()}`,
              sessionId: session.id,
              contextId: session.contextId,
              role: eventData.role,
              content: eventData.content,
              contentType: eventData.contentType || 'text',
              createdAt: eventData.createdAt,
            };
            setMessages(prev => {
              if (prev.some(m => m.id === msg.id)) return prev;
              return [...prev, msg];
            });
          } else if (eventType === 'ttsAudio' && eventData?.audio) {
            setError(null);
            ttsPlayback.play(eventData.audio);
          } else if (eventType === 'taskCreated' && eventData) {
            setTasks(prev => [eventData, ...prev]);
          } else if (eventType === 'documentCreated' && eventData) {
            onDocumentCreatedRef.current?.(eventData);
          } else if (eventType === 'error' && eventData) {
            setError(eventData.message || 'Audio-Fehler');
          }
        },
        (err) => {
          if (isMountedRef.current) {
            setError(err.message);
            setIsStreaming(false);
          }
        },
        () => {
          if (isMountedRef.current) {
            setIsStreaming(false);
            setStreamingStatus(null);
          }
        },
      );
    } catch (err: any) {
      if (isMountedRef.current) {
        setError(err.message);
        setIsStreaming(false);
      }
    }
  }, [instanceId, session]);

  const completeSessionCb = useCallback(async () => {
    if (!instanceId || !session) return;
    setActionLoading('completing');
    try {
      const completed = await completeSessionApi(request, instanceId, session.id);
      if (isMountedRef.current) {
        setMessages([]);
        setSession(completed);
        if (selectedContextId) await selectContext(selectedContextId, { skipSessionResume: true });
      }
    } catch (err: any) {
      if (isMountedRef.current) setError(err.message || 'Fehler beim Abschliessen');
    } finally {
      if (isMountedRef.current) setActionLoading(null);
    }
  }, [request, instanceId, session, selectedContextId, selectContext]);

  const cancelSessionCb = useCallback(async () => {
    if (!instanceId || !session) return;
    setActionLoading('cancelling');
    try {
      await cancelSessionApi(request, instanceId, session.id);
      if (isMountedRef.current) {
        setSession(null);
        setMessages([]);
      }
    } catch (err: any) {
      if (isMountedRef.current) setError(err.message || 'Fehler beim Abbrechen');
    } finally {
      if (isMountedRef.current) setActionLoading(null);
    }
  }, [request, instanceId, session]);

  const toggleTaskStatus = useCallback(async (taskId: string, currentStatus: string) => {
    if (!instanceId) return;
    setActionLoading('togglingTask');
    const newStatus = currentStatus === 'done' ? 'open' : 'done';
    try {
      const updated = await updateTaskStatusApi(request, instanceId, taskId, newStatus);
      if (isMountedRef.current) {
        setTasks(prev => prev.map(t => t.id === taskId ? updated : t));
      }
    } catch (err: any) {
      if (isMountedRef.current) setError(err.message);
    } finally {
      if (isMountedRef.current) setActionLoading(null);
    }
  }, [request, instanceId]);

  const addTask = useCallback(async (title: string, description?: string) => {
    if (!instanceId || !selectedContextId) return;
    setActionLoading('addingTask');
    try {
      const created = await createTaskApi(request, instanceId, selectedContextId, { title, description });
      if (isMountedRef.current) setTasks(prev => [created, ...prev]);
    } catch (err: any) {
      if (isMountedRef.current) setError(err.message);
    } finally {
      if (isMountedRef.current) setActionLoading(null);
    }
  }, [request, instanceId, selectedContextId]);

  const removeTask = useCallback(async (taskId: string) => {
    if (!instanceId) return;
    try {
      await deleteTaskApi(request, instanceId, taskId);
      if (isMountedRef.current) setTasks(prev => prev.filter(t => t.id !== taskId));
    } catch (err: any) {
      if (isMountedRef.current) setError(err.message);
    }
  }, [request, instanceId]);

  useEffect(() => { if (instanceId) refreshContexts(); }, [instanceId, refreshContexts]);

  return {
    contexts, selectedContextId, selectedContext, loadingContexts,
    session, messages, isStreaming, streamingStatus, streamingMessage,
    tasks, scores, sessions,
    error, inputValue, setInputValue,
    agentToolCalls,
    selectContext, createContext, archiveContext,
    startSession: startSessionCb,
    sendMessage, sendAudio,
    completeSession: completeSessionCb, cancelSession: cancelSessionCb,
    stopTts, pauseTts, resumeTts, hasAudioToResume,
    ttsIsPlaying: ttsPlayback.isPlaying, ttsIsPaused: ttsPlayback.isPaused,
    onTtsEventRef,
    actionLoading,
    toggleTaskStatus, addTask, removeTask,
    onDocumentCreatedRef,
    refreshContexts,
  };
}

async function _unlockAudioForTts(): Promise<void> {
  try {
    const ctx = new (window.AudioContext || (window as any).webkitAudioContext)();
    if (ctx.state === 'suspended') await ctx.resume();
    const silent = new Audio('data:audio/wav;base64,UklGRiQAAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQAAAAA=');
    silent.volume = 0;
    await silent.play();
  } catch {
    // Ignore if audio unlock fails
  }
}
