import AppShell from './AppShell'
import { useState, useEffect, useCallback, useRef } from 'react'
import { api, getToken, saveToken } from './utils/api'
import Sidebar from './components/Sidebar'
import TopBar from './components/TopBar'
import ChatArea from './components/ChatArea'
import Composer from './components/Composer'
import AuthModal from './components/AuthModal'
import ModelPicker from './components/ModelPicker'
import FeedbackModal from './components/FeedbackModal'
import SettingsModal from './components/SettingsModal'

function newUid() {
  return crypto.randomUUID ? crypto.randomUUID()
    : 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
        const r = Math.random() * 16 | 0; return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16)
      })
}

function LegacyApp() {
  /* ── Auth ─────────────────────────────────── */
  const [token,   setToken]   = useState(() => getToken())
  const [user,    setUser]    = useState(null)
  const [authMode, setAuthMode] = useState(null) // 'login' | 'register' | null

  /* ── Sessions ─────────────────────────────── */
  const [sessions,          setSessions]         = useState([])
  const [currentSessionId,  setCurrentSessionId] = useState(null)
  const [sessionTitle,      setSessionTitle]     = useState('New Chat')

  /* ── Messages ─────────────────────────────── */
  const [messages,  setMessages]  = useState([])
  const [loading,   setLoading]   = useState(false)

  /* ── Model registry ───────────────────────── */
  const [registry,  setRegistry]  = useState({})
  const [provider,  setProvider]  = useState('groq')
  const [model,     setModel]     = useState('llama-3.3-70b-versatile')
  const [agentMode, setAgentMode] = useState(false)

  /* ── Attachments ──────────────────────────── */
  const [attachments, setAttachments] = useState([])

  /* ── UI state ─────────────────────────────── */
  const [sidebarOpen,  setSidebarOpen]  = useState(true)
  const [modal,        setModal]        = useState(null)  // 'model'|'feedback'|'settings'
  const [theme,        setTheme]        = useState(() => localStorage.getItem('owngpt_v2_theme') || 'dark')
  const [searchQ,      setSearchQ]      = useState('')

  const chatRef = useRef(null)

  /* ── Apply theme ──────────────────────────── */
  useEffect(() => {
    document.documentElement.setAttribute('data-theme', theme)
    localStorage.setItem('owngpt_v2_theme', theme)
  }, [theme])

  /* ── Boot: capture URL token → load me + models ── */
  useEffect(() => {
    const params = new URLSearchParams(window.location.search)
    const urlToken = params.get('access_token')
    if (urlToken) {
      saveToken(urlToken); setToken(urlToken)
      window.history.replaceState({}, '', window.location.pathname)
    }
    // load model registry (always)
    api.get('/models').then(d => {
      if (d?.providers) setRegistry(d.providers)
    }).catch(() => {})
  }, [])

  /* ── Auth check ───────────────────────────── */
  useEffect(() => {
    if (!token) { setUser(null); return }
    api.get('/auth/me').then(me => {
      setUser(me)
      loadSessions('')
    }).catch(() => {
      saveToken(null); setToken(null); setUser(null)
    })
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [token])

  /* ── Session search ───────────────────────── */
  useEffect(() => {
    if (!user) return
    const t = setTimeout(() => loadSessions(searchQ), 300)
    return () => clearTimeout(t)
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [searchQ, user])

  /* ── Helpers ──────────────────────────────── */
  const scrollBottom = useCallback(() => {
    setTimeout(() => { chatRef.current?.scrollTo({ top: 9999999, behavior: 'smooth' }) }, 30)
  }, [])

  const loadSessions = useCallback(async (q) => {
    const url = q ? `/search?query=${encodeURIComponent(q)}` : '/search'
    try { const d = await api.get(url); setSessions(d.sessions || []) } catch {}
  }, [])

  /* ── Auth handlers ────────────────────────── */
  const handleLogin = useCallback(async (username, password) => {
    const d = await api.post('/auth/login', { username, password })
    saveToken(d.access_token); setToken(d.access_token)
    setAuthMode(null)
  }, [])

  const handleRegister = useCallback(async (username, password) => {
    await api.post('/auth/register', { username, password })
    await handleLogin(username, password)
  }, [handleLogin])

  const handleLogout = useCallback(() => {
    saveToken(null); setToken(null); setUser(null)
    setSessions([]); setMessages([]); setCurrentSessionId(null); setSessionTitle('New Chat')
  }, [])

  /* ── Session handlers ─────────────────────── */
  const handleNewSession = useCallback(() => {
    setCurrentSessionId(null); setMessages([]); setSessionTitle('New Chat'); setAttachments([])
    setSearchQ('')
  }, [])

  const handleSelectSession = useCallback(async (s) => {
    setCurrentSessionId(s.session_id); setSessionTitle(s.title || 'New Chat')
    setSessions(prev => prev.map(x => ({ ...x, _active: x.session_id === s.session_id })))
    setMessages([])
    try {
      const d = await api.get(`/history/${encodeURIComponent(s.session_id)}`)
      setMessages(d.messages || [])
    } catch {}
    scrollBottom()
  }, [scrollBottom])

  const handleDeleteSession = useCallback(async (sessionId) => {
    if (!window.confirm('Delete this chat? This cannot be undone.')) return
    try { await api.delete(`/session/${encodeURIComponent(sessionId)}`) } catch {}
    if (currentSessionId === sessionId) handleNewSession()
    setSessions(prev => prev.filter(s => s.session_id !== sessionId))
  }, [currentSessionId, handleNewSession])

  const handleRenameSession = useCallback(async (newTitle) => {
    if (!currentSessionId) return
    try { await api.patch(`/session/${encodeURIComponent(currentSessionId)}/rename`, { title: newTitle }) } catch {}
    setSessionTitle(newTitle)
    setSessions(prev => prev.map(s => s.session_id === currentSessionId ? { ...s, title: newTitle } : s))
  }, [currentSessionId])

  /* ── File upload ──────────────────────────── */
  const handleUpload = useCallback(async (files) => {
    for (const file of Array.from(files)) {
      const form = new FormData(); form.append('file', file)
      try {
        const d = await api.postForm('/upload', form)
        setAttachments(prev => [...prev, { filename: d.filename, kind: d.kind, extracted_text: d.extracted_text }])
      } catch (e) { alert('Upload failed: ' + e.message) }
    }
  }, [])

  /* ── Send message ─────────────────────────── */
  const handleSend = useCallback(async (text) => {
    if (!user) { setAuthMode('login'); return }
    if (!text.trim() && attachments.length === 0) return

    const sessionId = currentSessionId || newUid()
    if (!currentSessionId) {
      setCurrentSessionId(sessionId)
      setSessionTitle(text.slice(0, 48) || 'New Chat')
    }

    const userMsg = {
      role: 'user', content: text, session_id: sessionId,
      attachments: [...attachments], _id: newUid(),
    }
    setMessages(prev => [...prev, userMsg])
    setAttachments([])
    setLoading(true)
    scrollBottom()

    try {
      const respId = newUid();
      setMessages(prev => [...prev, {
        role: 'assistant', content: '',
        model_used: model, provider,
        session_id: sessionId, _id: respId,
      }]);

      const response = await fetch('/chat', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          ...(getToken() ? { 'Authorization': `Bearer ${getToken()}` } : {})
        },
        body: JSON.stringify({
          message: text, session_id: sessionId,
          provider, model,
          agent_mode: agentMode,
          attachments: attachments.map(a => ({ filename: a.filename, kind: a.kind, extracted_text: a.extracted_text })),
        })
      });

      if (!response.ok) throw new Error(`Server returned ${response.status}`);
      const reader = response.body.getReader();
      const decoder = new TextDecoder();
      let done = false;
      let fullText = "";
      let buffer = "";

      while (!done) {
        const { value, done: doneReading } = await reader.read();
        done = doneReading;
        if (value) {
          buffer += decoder.decode(value, { stream: true });
          let boundaries = buffer.split(/\r?\n\r?\n/);
          buffer = boundaries.pop() || ""; // retain incomplete event in buffer
          
          for (let block of boundaries) {
            const lines = block.split(/\r?\n/);
            let dataStr = null;
            for (let line of lines) {
              if (line.startsWith('data: ')) {
                dataStr = line.slice(6);
              }
            }
            if (!dataStr) continue;
            
            try {
              const data = JSON.parse(dataStr);
              if (data.text) {
                fullText += data.text;
                // incrementally update state so UI renders instantly
                setMessages(prev => prev.map(m => m._id === respId ? { ...m, content: fullText } : m));
              } else if (data.model_used) {
                // Done event metadata payload
                setMessages(prev => prev.map(m => m._id === respId ? { 
                  ...m, 
                  model_used: data.model_used, 
                  provider: data.provider,
                  content: data.response ? data.response : m.content
                } : m));
              } else if (data.detail) {
                // Error event 
                fullText += `\n\n⚠️ Error: ${data.detail}`;
                setMessages(prev => prev.map(m => m._id === respId ? { ...m, content: fullText } : m));
              }
            } catch (e) {
              console.warn("Failed to parse SSE data block", e, dataStr);
            }
          }
        }
      }
      loadSessions(searchQ)
    } catch (e) {
      setMessages(prev => [...prev, { role: 'assistant', content: `⚠️ Error: ${e.message}`, _id: newUid() }])
    } finally {
      setLoading(false); scrollBottom()
    }
  }, [user, currentSessionId, attachments, provider, model, agentMode, scrollBottom, loadSessions, searchQ])

  /* ── Feedback ─────────────────────────────── */
  const handleFeedback = useCallback(async (data) => {
    await api.post('/feedback', data)
  }, [])

  /* ── Model select ─────────────────────────── */
  const handleModelSelect = useCallback((prov, mod) => {
    setProvider(prov); setModel(mod)
    setModal(null)
  }, [])

  const requireAuth = useCallback((action) => {
    if (!user) { setAuthMode('login'); return false }
    action?.()
    return true
  }, [user])

  return (
    <div className="app-shell">
      <div className="bg-mesh"></div>
      <Sidebar
        open={sidebarOpen}
        sessions={sessions}
        currentSessionId={currentSessionId}
        searchQ={searchQ}
        onSearchChange={setSearchQ}
        onNewSession={handleNewSession}
        onSelectSession={handleSelectSession}
        onDeleteSession={handleDeleteSession}
        onFeedback={() => requireAuth(() => setModal('feedback'))}
        onSettings={() => setModal('settings')}
      />

      <div className="main">
        <TopBar
          onToggleSidebar={() => setSidebarOpen(o => !o)}
          sessionTitle={sessionTitle}
          user={user}
          onRename={handleRenameSession}
          onLogin={() => setAuthMode('login')}
          onLogout={handleLogout}
          onFeedback={() => requireAuth(() => setModal('feedback'))}
          onSettings={() => setModal('settings')}
          theme={theme}
          onTheme={setTheme}
          currentSessionId={currentSessionId}
        />

        <ChatArea
          ref={chatRef}
          messages={messages}
          loading={loading}
          user={user}
          onSuggestion={t => { requireAuth(() => handleSend(t)) }}
        />

        <Composer
          onSend={handleSend}
          onUpload={handleUpload}
          attachments={attachments}
          onRemoveAttachment={i => setAttachments(prev => prev.filter((_, idx) => idx !== i))}
          provider={provider}
          model={model}
          registry={registry}
          agentMode={agentMode}
          onToggleAgent={() => setAgentMode(a => !a)}
          onOpenModelPicker={() => setModal('model')}
          disabled={!user}
          onClickDisabled={() => setAuthMode('login')}
        />
      </div>

      {/* ── Modals ── */}
      {authMode && (
        <AuthModal
          mode={authMode}
          onModeChange={setAuthMode}
          onLogin={handleLogin}
          onRegister={handleRegister}
          onClose={() => setAuthMode(null)}
        />
      )}
      {modal === 'model' && (
        <ModelPicker
          registry={registry}
          currentProvider={provider}
          currentModel={model}
          onSelect={handleModelSelect}
          onClose={() => setModal(null)}
        />
      )}
      {modal === 'feedback' && (
        <FeedbackModal
          onSubmit={handleFeedback}
          onClose={() => setModal(null)}
        />
      )}
      {modal === 'settings' && (
        <SettingsModal
          theme={theme}
          onTheme={setTheme}
          onClearChats={() => {
            if (window.confirm('Clear the current chat from this screen? Saved history stays in the sidebar.')) {
              setMessages([]); setCurrentSessionId(null); setSessionTitle('New Chat'); setAttachments([])
            }
          }}
          onClose={() => setModal(null)}
        />
      )}
    </div>
  )
}

export default AppShell
