import React, { useEffect, useState } from 'react'

import logoIcon from '../../design/assets/logo-icon.png'
import type { IssueChange } from '@commitea/core'

import type { PublicConfig } from '../../global.js'
import type { IssueRef } from '../../data/fixtures.js'
import { ConnectScreen } from '../screens/connect-screen.js'
import {
  backlogCalibration,
  capacityWorkers,
  forecastBacklog,
  issuesToBoardColumns,
  milestoneView,
  runwayView,
  scheduleFocus,
} from '../../lib/backlog.js'
import { useBacklog } from '../../lib/use-backlog.js'
import { useCapacity } from '../../lib/use-capacity.js'
import { PrimitivesGallery } from '../gallery.js'
import { BoardScreen } from '../screens/board-screen.js'
import { CalibrationScreen } from '../screens/calibration-screen.js'
import { CaptureScreen } from '../screens/capture-screen.js'
import { DirectivesScreen } from '../screens/directives-screen.js'
import { FocusScreen } from '../screens/focus-screen.js'
import { InboxScreen } from '../screens/inbox-screen.js'
import { IssueScreen } from '../screens/issue-screen.js'
import { MilestoneScreen } from '../screens/milestone-screen.js'
import { OnboardingScreen } from '../screens/onboarding-screen.js'
import { RunwayScreen } from '../screens/runway-screen.js'
import { SettingsScreen } from '../screens/settings-screen.js'
import { StandupScreen } from '../screens/standup-screen.js'
import { Icon, Switch } from '../ui/index.js'
import { ChatPanel } from './chat-panel.js'
import { PlaceholderScreen } from './placeholder-screen.js'
import { OfflineBanner, StatesScreen } from './states.js'

type View =
  | 'standup'
  | 'focus'
  | 'inbox'
  | 'capture'
  | 'board'
  | 'runway'
  | 'directives'
  | 'settings'
  | 'states'
  | 'firstrun'
  | 'primitives'
  | 'issue'
  | 'calibration'
  | 'milestone'

interface NavEntry {
  id: View
  label: string
  icon: string
  count?: number | null
}

// which phase builds each not-yet-real view (shown on its placeholder)
const PHASE: Partial<Record<View, string>> = {
  standup: 'P3-3',
  focus: 'P3-3',
  inbox: 'P3-6',
  capture: 'P3-7',
  board: 'P3-4',
  runway: 'P3-5',
  directives: 'P3-8',
  settings: 'P3-8',
  firstrun: 'P3-8',
  issue: 'P3-6',
  calibration: 'P3-5',
  milestone: 'P3-5',
}

const TITLE: Partial<Record<View, string>> = {
  standup: 'Standup',
  focus: 'Morning service',
  inbox: 'Inbox',
  capture: 'Capture',
  board: 'The pot',
  runway: 'Runway',
  directives: 'Directives',
  settings: 'Settings',
  firstrun: 'First run',
  issue: 'Issue',
  calibration: 'Calibration',
  milestone: 'Milestone',
}

const INBOX_UNREAD = 3

export function AppShell() {
  const [view, setView] = useState<View>('focus')
  const [prevView, setPrevView] = useState<View>('focus')
  const [dark, setDark] = useState(false)
  const [offline, setOffline] = useState(false)
  const [issue, setIssue] = useState<IssueRef | null>(null)
  const [readIds, setReadIds] = useState<number[]>([])
  const [milestoneId, setMilestoneId] = useState<number | null>(null)
  const [gate, setGate] = useState<'checking' | 'connect' | 'ready'>('checking')
  const [pubConfig, setPubConfig] = useState<PublicConfig | null>(null)
  const [backlog, refetchBacklog] = useBacklog()
  const capacityMembers = useCapacity()
  const workers = capacityWorkers(capacityMembers)
  const boardColumns =
    backlog.status === 'ready' ? issuesToBoardColumns(backlog.issues, backlog.timelines) : undefined
  const focus =
    backlog.status === 'ready' ? scheduleFocus(backlog.issues, backlog.deps, backlog.timelines) : undefined
  const calibration =
    backlog.status === 'ready' ? backlogCalibration(backlog.issues, backlog.timelines) : undefined
  const forecast =
    backlog.status === 'ready'
      ? (forecastBacklog(backlog.issues, backlog.deps, new Date(), calibration?.model, workers) ?? undefined)
      : undefined
  const runwayMilestones =
    backlog.status === 'ready'
      ? runwayView(backlog.issues, backlog.milestones, backlog.deps, new Date(), workers)
      : undefined
  const milestone =
    backlog.status === 'ready' && milestoneId != null
      ? (milestoneView(
          milestoneId,
          backlog.issues,
          backlog.milestones,
          backlog.deps,
          backlog.timelines,
          calibration?.model,
          new Date(),
          workers,
        ) ?? undefined)
      : undefined

  useEffect(() => {
    document.documentElement.setAttribute('data-theme', dark ? 'dark' : 'light')
  }, [dark])

  // Gate: e2e/demo → the shell (fixtures); configured → the shell (real data);
  // otherwise → the connect screen (each teammate brings their own token).
  useEffect(() => {
    window.commitea.gitea
      .status()
      .then((s) => setGate(s.demo || s.configured ? 'ready' : 'connect'))
      .catch(() => setGate('connect'))
    window.commitea.config.get().then(setPubConfig).catch(() => {})
  }, [])

  const openIssue = (ref: IssueRef) => {
    if (view !== 'issue') setPrevView(view)
    setIssue(ref)
    setView('issue')
  }

  // The write path: apply through the bridge, reflect the new labels on the open
  // issue immediately, and re-reconcile so the board + forecast catch up.
  const applyChange = async (change: IssueChange) => {
    const res = await window.commitea.gitea.applyChange(change)
    if (res.ok) {
      setIssue((cur) => (cur && cur.id === res.issue.number ? { ...cur, labels: res.issue.labels } : cur))
      refetchBacklog()
    }
    return res
  }

  const NAV: NavEntry[] = [
    { id: 'standup', label: 'Standup', icon: 'sun' },
    { id: 'focus', label: 'Morning service', icon: 'coffee' },
    { id: 'inbox', label: 'Inbox', icon: 'bell', count: INBOX_UNREAD || null },
    { id: 'capture', label: 'Capture', icon: 'plus' },
    { id: 'board', label: 'The pot', icon: 'square-kanban' },
    { id: 'runway', label: 'Runway', icon: 'chart-line' },
    { id: 'directives', label: 'Directives', icon: 'flag' },
  ]

  const NavItem = ({ item }: { item: NavEntry }) => {
    const active =
      view === item.id ||
      (view === 'issue' && prevView === item.id) ||
      ((view === 'calibration' || view === 'milestone') && item.id === 'runway')
    return (
      <button
        type="button"
        onClick={() => setView(item.id)}
        aria-current={active ? 'page' : undefined}
        style={{
          display: 'flex',
          alignItems: 'center',
          gap: 10,
          width: '100%',
          font: `${active ? 600 : 500} 13.5px/1 var(--font-sans)`,
          color: active ? 'var(--ink-1)' : 'var(--ink-2)',
          background: active ? 'var(--paper-2)' : 'transparent',
          border: 'none',
          borderRadius: 'var(--radius-2)',
          padding: '9px 12px',
          cursor: 'pointer',
          textAlign: 'left',
          transition: 'background var(--duration-fast) var(--ease-out)',
        }}
      >
        <Icon name={item.icon} size={16} style={{ color: active ? 'var(--accent-text)' : 'var(--ink-3)' }} />
        {item.label}
        {item.count ? (
          <span
            style={{
              marginLeft: 'auto',
              font: '500 10.5px/16px var(--font-mono)',
              color: 'var(--accent-text)',
              background: 'var(--spruce-2)',
              borderRadius: 'var(--radius-round)',
              padding: '0 6px',
            }}
          >
            {item.count}
          </span>
        ) : null}
      </button>
    )
  }

  const renderScreen = () => {
    switch (view) {
      case 'focus':
        return <FocusScreen onOpenIssue={openIssue} focus={focus} forecast={forecast} />
      case 'standup':
        return <StandupScreen onBegin={() => setView('focus')} onOpenIssue={openIssue} />
      case 'board':
        return (
          <BoardScreen
            onOpenIssue={openIssue}
            columns={boardColumns}
            loading={backlog.status === 'loading'}
          />
        )
      case 'runway':
        return (
          <RunwayScreen
            onOpenCalibration={() => setView('calibration')}
            onOpenMilestone={(id) => {
              setMilestoneId(id ?? null)
              setView('milestone')
            }}
            calibration={calibration ? { n: calibration.model.n, coldStart: calibration.model.coldStart } : undefined}
            milestones={runwayMilestones}
            capacity={capacityMembers}
          />
        )
      case 'calibration':
        return <CalibrationScreen onBack={() => setView('runway')} data={calibration?.data} />
      case 'milestone':
        return <MilestoneScreen onBack={() => setView('runway')} onOpenIssue={openIssue} data={milestone} />
      case 'inbox':
        return (
          <InboxScreen
            onOpenIssue={openIssue}
            onOpenDirectives={() => setView('directives')}
            readIds={readIds}
            setReadIds={setReadIds}
          />
        )
      case 'capture':
        return <CaptureScreen onDone={() => setView('focus')} />
      case 'directives':
        return <DirectivesScreen />
      case 'settings':
        return (
          <SettingsScreen
            dark={dark}
            setDark={setDark}
            connection={pubConfig}
            onReconnect={() => setGate('connect')}
            onDisconnect={() => {
              void window.commitea.config.clear().then(() => {
                setPubConfig(null)
                setGate('connect')
              })
            }}
          />
        )
      case 'issue':
        return issue ? (
          <IssueScreen
            issue={issue}
            onBack={() => setView(prevView)}
            onOpenIssue={openIssue}
            canWrite={backlog.status === 'ready'}
            onApplyChange={applyChange}
          />
        ) : null
      case 'states':
        return <StatesScreen onCapture={() => setView('capture')} />
      case 'primitives':
        return <PrimitivesGallery />
      default:
        return <PlaceholderScreen title={TITLE[view] ?? view} phase={PHASE[view] ?? 'a later phase'} />
    }
  }

  // Connection gate (real onboarding) comes before everything else.
  if (gate === 'checking') {
    return <div style={{ height: '100vh', background: 'var(--surface-app)' }} />
  }
  if (gate === 'connect') {
    return (
      <ConnectScreen
        existing={pubConfig}
        onConnected={() => {
          window.commitea.config.get().then(setPubConfig).catch(() => {})
          setGate('ready')
          refetchBacklog()
        }}
      />
    )
  }

  // First run is full-window — no rail, no chat panel
  if (view === 'firstrun') {
    return <OnboardingScreen onDone={(dest) => setView(dest)} />
  }

  return (
    <div
      data-screen-label={`app-${view}`}
      style={{
        display: 'flex',
        height: '100vh',
        minWidth: 1280,
        background: 'var(--surface-app)',
        overflow: 'hidden',
      }}
    >
      {/* left rail */}
      <nav
        aria-label="Primary"
        style={{
          width: 208,
          flexShrink: 0,
          display: 'flex',
          flexDirection: 'column',
          gap: 4,
          padding: '18px 12px 14px',
          borderRight: '1px solid var(--line-1)',
        }}
      >
        <div style={{ display: 'flex', alignItems: 'center', gap: 9, padding: '0 12px 16px' }}>
          <img src={logoIcon} width="24" height="24" alt="" style={{ borderRadius: 6, display: 'block' }} />
          <span style={{ font: '400 21px/1 var(--font-serif-display)', color: 'var(--ink-1)' }}>
            Commi<span style={{ color: 'var(--accent-text)' }}>Tea</span>
          </span>
        </div>

        {NAV.map((n) => (
          <NavItem key={n.id} item={n} />
        ))}

        <div style={{ borderTop: '1px solid var(--line-1)', margin: '10px 8px' }} />
        <NavItem item={{ id: 'settings', label: 'Settings', icon: 'settings-2' }} />
        <NavItem item={{ id: 'firstrun', label: 'First run', icon: 'play' }} />
        <NavItem item={{ id: 'states', label: 'States', icon: 'circle-dashed' }} />
        <NavItem item={{ id: 'primitives', label: 'Primitives', icon: 'layers' }} />

        <div style={{ marginTop: 'auto', padding: '0 12px', display: 'flex', flexDirection: 'column', gap: 12 }}>
          <button
            type="button"
            onClick={() => setOffline((o) => !o)}
            title="Toggle the connection (demo)"
            aria-label={offline ? 'Connection: offline' : 'Connection: online'}
            style={{
              font: '400 11px var(--font-mono)',
              color: 'var(--ink-3)',
              display: 'flex',
              alignItems: 'center',
              gap: 6,
              background: 'none',
              border: 'none',
              padding: 0,
              cursor: 'pointer',
              textAlign: 'left',
            }}
          >
            <span
              style={{
                width: 6,
                height: 6,
                borderRadius: '50%',
                background: offline ? 'var(--danger)' : 'var(--ok)',
                display: 'inline-block',
              }}
            />
            gitea.stephenmann.io
          </button>
          <Switch
            label={<span style={{ font: 'var(--text-caption)' }}>Evening service</span>}
            checked={dark}
            onChange={(e) => setDark(e.target.checked)}
          />
        </div>
      </nav>

      {/* main */}
      <main style={{ flex: 1, minWidth: 0, overflowY: 'auto', padding: '24px 28px' }}>
        <div
          style={{
            maxWidth: 1120,
            margin: '0 auto',
            height: view === 'board' ? '100%' : 'auto',
            display: 'flex',
            flexDirection: 'column',
            gap: 14,
          }}
        >
          {offline ? <OfflineBanner /> : null}
          {renderScreen()}
        </div>
      </main>

      <ChatPanel onOpenDirectives={() => setView('directives')} offline={offline} onApplyChange={applyChange} />
    </div>
  )
}
