import { useState, useEffect, createElement, lazy, Suspense } from 'react'
import { BrowserRouter, Routes, Route, Navigate, useParams } from 'react-router-dom'
import { api, setCredentials, clearCredentials } from './api'
import Sidebar from './components/Sidebar.jsx'
import BottomNav from './components/BottomNav.jsx'
import SourceTabs from './components/SourceTabs.jsx'
import Login from './pages/Login'
import SourceList from './pages/SourceList'
import SourceDetail from './pages/SourceDetail'
import Bridge from './pages/Bridge'
import ImportHub from './pages/ImportHub'
import Import from './pages/Import'
import Rules from './pages/Rules'
import Mappings from './pages/Mappings'
import Records from './pages/Records'
import Log from './pages/Log'
const Pivot = lazy(() => import('./pages/Pivot'))
import Remap from './pages/Remap'
import Stacks from './pages/Stacks'

// Source-scoped pages still take a `source` prop; this reads it off the URL so
// they didn't all need rewriting when selection moved out of the status bar.
function ScopedToSource({ component, ...props }) {
  const { name } = useParams()
  return createElement(component, { source: name, ...props })
}

// Pivot doubles as the stack viewer; a stack in the URL takes precedence there
function StackPivot() {
  const { name } = useParams()
  return <Pivot source={name} selectedStack={name} setSelectedStack={() => {}} />
}

export default function App() {
  const [authed, setAuthed] = useState(false)
  const [loginUser, setLoginUser] = useState('')
  const [sources, setSources] = useState([])
  const [source, setSource] = useState(() => localStorage.getItem('selectedSource') || '')
  const [sidebarExpanded, setSidebarExpanded] = useState(() => localStorage.getItem('df_sidebar') !== 'collapsed')
  // Sets of names whose dfv view is out of sync with current definitions
  const [staleSources, setStaleSources] = useState(new Set())
  const [staleStacks, setStaleStacks] = useState(new Set())
  const [reprocessSources, setReprocessSources] = useState(new Set())
  const [generating, setGenerating] = useState({}) // { 'source:name': true }

  async function handleLogin(user, pass) {
    setCredentials(user, pass)
    const s = await api.getSources()
    sessionStorage.setItem('df_user', user)
    sessionStorage.setItem('df_pass', pass)
    setSources(s)
    if (!source && s.length > 0) setSource(s[0].name)
    setAuthed(true)
    setLoginUser(user)
  }

  function handleLogout() {
    clearCredentials()
    sessionStorage.removeItem('df_user')
    sessionStorage.removeItem('df_pass')
    setAuthed(false)
    setLoginUser('')
    setSources([])
    setStaleSources(new Set())
    setStaleStacks(new Set())
    setReprocessSources(new Set())
  }

  // Load initial stale state from DB once on login
  useEffect(() => {
    if (!authed) return
    api.getStatus().then(s => {
      setStaleSources(new Set((s.stale_sources || []).map(x => x.name)))
      setStaleStacks(new Set((s.stale_stacks || []).map(x => x.name)))
    }).catch(() => {})
  }, [authed])

  function markSourceStale(name) {
    setStaleSources(prev => new Set([...prev, name]))
  }
  function markNeedsReprocess(name) {
    setReprocessSources(prev => new Set([...prev, name]))
  }
  async function handleReprocessSource(name) {
    setGenerating(g => ({ ...g, [`rp:${name}`]: true }))
    try {
      await api.reprocess(name)
      setReprocessSources(prev => { const n = new Set(prev); n.delete(name); return n })
    } catch (e) { alert(e.message) }
    finally { setGenerating(g => { const n = { ...g }; delete n[`rp:${name}`]; return n }) }
  }
  function markStackStale(name) {
    setStaleStacks(prev => new Set([...prev, name]))
  }
  function clearStackStale(name) {
    setStaleStacks(prev => { const n = new Set(prev); n.delete(name); return n })
  }

  async function handleGenerateSource(name) {
    setGenerating(g => ({ ...g, [`src:${name}`]: true }))
    try {
      await api.generateView(name)
      setStaleSources(prev => { const n = new Set(prev); n.delete(name); return n })
    } catch (e) { alert(e.message) }
    finally { setGenerating(g => { const n = { ...g }; delete n[`src:${name}`]; return n }) }
  }

  async function handleGenerateStack(name) {
    setGenerating(g => ({ ...g, [`stk:${name}`]: true }))
    try {
      await api.generateStackView(name)
      setStaleStacks(prev => { const n = new Set(prev); n.delete(name); return n })
    } catch (e) { alert(e.message) }
    finally { setGenerating(g => { const n = { ...g }; delete n[`stk:${name}`]; return n }) }
  }

  // On mount, restore session if credentials are saved
  useEffect(() => {
    const user = sessionStorage.getItem('df_user')
    const pass = sessionStorage.getItem('df_pass')
    if (user && pass) handleLogin(user, pass).catch(() => handleLogout())
  }, [])

  useEffect(() => {
    if (source) localStorage.setItem('selectedSource', source)
  }, [source])

  useEffect(() => {
    localStorage.setItem('df_sidebar', sidebarExpanded ? 'expanded' : 'collapsed')
  }, [sidebarExpanded])

  if (!authed) return <Login onLogin={handleLogin} />

  return (
    <BrowserRouter>
      <div className="flex h-screen">

        <div className="hidden md:flex">
        <Sidebar
          expanded={sidebarExpanded}
          setExpanded={setSidebarExpanded}
          loginUser={loginUser}
          onLogout={handleLogout}
        />
        </div>

        {/* Main */}
        <div className="flex-1 overflow-hidden flex flex-col min-w-0">
          {(staleSources.size > 0 || staleStacks.size > 0) && (
            <div className="bg-warn-soft border-b border-warn-line px-4 py-1.5 text-xs text-warn flex flex-wrap items-center gap-x-3 gap-y-1">
              <span className="font-medium">View out of sync:</span>
              {[...staleSources].map(name => (
                <span key={name} className="flex items-center gap-1">
                  {name}
                  <button
                    onClick={() => handleGenerateSource(name)}
                    disabled={generating[`src:${name}`]}
                    className="px-1.5 py-0.5 rounded bg-warn-line hover:bg-warn-line disabled:opacity-50 font-medium"
                  >
                    {generating[`src:${name}`] ? '…' : 'Generate'}
                  </button>
                </span>
              ))}
              {staleSources.size > 0 && staleStacks.size > 0 && <span className="text-warn">|</span>}
              {[...staleStacks].map(name => (
                <span key={name} className="flex items-center gap-1">
                  stack: {name}
                  <button
                    onClick={() => handleGenerateStack(name)}
                    disabled={generating[`stk:${name}`]}
                    className="px-1.5 py-0.5 rounded bg-warn-line hover:bg-warn-line disabled:opacity-50 font-medium"
                  >
                    {generating[`stk:${name}`] ? '…' : 'Generate'}
                  </button>
                </span>
              ))}
            </div>
          )}
          {reprocessSources.size > 0 && (
            <div className="bg-accent-soft border-b border-accent-line px-4 py-1.5 text-xs text-accent flex flex-wrap items-center gap-x-3 gap-y-1">
              <span className="font-medium">Mappings updated:</span>
              {[...reprocessSources].map(name => (
                <span key={name} className="flex items-center gap-1">
                  {name}
                  <button
                    onClick={() => handleReprocessSource(name)}
                    disabled={generating[`rp:${name}`]}
                    className="px-1.5 py-0.5 rounded bg-blue-200 hover:bg-blue-300 disabled:opacity-50 font-medium"
                  >
                    {generating[`rp:${name}`] ? '…' : 'Reprocess'}
                  </button>
                </span>
              ))}
            </div>
          )}

          <div className="flex-1 overflow-auto pb-14 md:pb-0">
            <Suspense fallback={<div className="p-6 text-sm text-muted">Loading…</div>}>
            <Routes>
              <Route path="/" element={<Navigate to="/sources" replace />} />

              <Route path="/sources" element={<SourceList sources={sources} setSources={setSources} setSource={setSource} />} />
              <Route path="/sources/:name" element={<SourceTabs sources={sources} />}>
                <Route index element={<SourceDetail sources={sources} setSources={setSources} />} />
                <Route path="import" element={<ScopedToSource component={Import} />} />
                <Route path="rules" element={<ScopedToSource component={Rules} onStale={markSourceStale} />} />
                <Route path="mappings" element={<ScopedToSource component={Mappings} onNeedsReprocess={markNeedsReprocess} />} />
                <Route path="records" element={<ScopedToSource component={Records} />} />
                <Route path="pivot" element={<ScopedToSource component={Pivot} />} />
              </Route>

              <Route path="/import" element={<ImportHub sources={sources} />} />
              <Route path="/bridge" element={<Bridge sources={sources} />} />
              <Route path="/stacks" element={<Stacks sources={sources} onStackStale={markStackStale} onStackViewGenerated={clearStackStale} />} />
              <Route path="/stacks/:name/pivot" element={<StackPivot />} />
              <Route path="/remap" element={<Remap />} />
              <Route path="/log" element={<Log />} />
            </Routes>
            </Suspense>
          </div>
        </div>
      </div>
      <BottomNav />
    </BrowserRouter>
  )
}
