import { useDeferredValue, useEffect, useMemo, useState } from 'react'
import { useQuery } from '@tanstack/react-query'
import clsx from 'clsx'
import { AnimatePresence } from 'framer-motion'
import { Copy, Download, Link2, Sparkles, X } from 'lucide-react'
import ChatArea from './components/Chat/ChatArea'
import Composer from './components/Composer/Composer'
import Sidebar from './components/Sidebar/Sidebar'
import AuthDialog from './components/UI/AuthDialogPanel'
import AppHeader from './components/UI/AppHeader'
import Button from './components/UI/Button'
import ConfirmDialog from './components/UI/ConfirmDialog'
import FeedbackDialog from './components/UI/FeedbackDialog'
import Modal from './components/UI/Modal'
import ModelDialog from './components/UI/ModelDialog'
import Toaster from './components/UI/Toaster'
import WorkspacePanel from './components/UI/WorkspacePanel'
import { useAuth } from './hooks/useAuth'
import { useChat } from './hooks/useChat'
import { useInstallPrompt } from './hooks/useInstallPrompt'
import { decodeSharePayload, useAppStore } from './store/useAppStore'
import { api } from './utils/api'

function isSmallViewport() {
  return typeof window !== 'undefined' && window.innerWidth < 1024
}

function clearShareHash() {
  if (typeof window === 'undefined' || !window.location.hash.startsWith('#share=')) return
  window.history.replaceState({}, '', window.location.pathname + window.location.search)
}

function buildRenamePayload(sessionId, title) {
  return sessionId ? { session_id: sessionId, title } : { title, isDraft: true }
}

function getEnabledProviderIds(registry = {}) {
  return Object.entries(registry)
    .filter(([, providerEntry]) => providerEntry?.enabled !== false)
    .map(([providerId]) => providerId)
}

function getPreferredModelId(providerId, providerEntry, defaultProvider, defaultModel) {
  if (!providerEntry?.models?.length) {
    return defaultModel
  }
  if (providerId === defaultProvider && providerEntry.models.some((entry) => entry.id === defaultModel)) {
    return defaultModel
  }
  return providerEntry.models[0]?.id || defaultModel
}

function getInstallSteps(platform, instructions) {
  if (platform === 'ios') {
    return [
      'Open OwnGPT in Safari on your iPhone or iPad.',
      'Tap the Share button in the browser toolbar.',
      'Choose Add to Home Screen, then confirm Install.',
    ]
  }

  if (platform === 'android') {
    return [
      'Open the browser menu in Chrome or Edge.',
      'Choose Install app or Add to Home screen.',
      'Confirm the install prompt to keep OwnGPT on your device.',
    ]
  }

  return [
    'Open the browser menu near the address bar.',
    'Choose Install OwnGPT, Install app, or Create shortcut.',
    'Launch the installed app for a cleaner desktop workspace.',
  ]
}

export default function AppShell() {
  const {
    token,
    user,
    isAuthenticated,
    authMode,
    isAuthDialogOpen,
    openAuth,
    closeAuth,
    login,
    register,
    loginPending,
    registerPending,
    logout,
    loginWithGoogle,
    requireAuth,
  } = useAuth()

  const theme = useAppStore((state) => state.theme)
  const themePreference = useAppStore((state) => state.themePreference)
  const setThemePreference = useAppStore((state) => state.setThemePreference)
  const syncSystemTheme = useAppStore((state) => state.syncSystemTheme)
  const sidebarOpen = useAppStore((state) => state.sidebarOpen)
  const setSidebarOpen = useAppStore((state) => state.setSidebarOpen)
  const toggleSidebar = useAppStore((state) => state.toggleSidebar)
  const currentSessionId = useAppStore((state) => state.currentSessionId)
  const currentSessionTitle = useAppStore((state) => state.currentSessionTitle)
  const composerText = useAppStore((state) => state.composerText)
  const setComposerText = useAppStore((state) => state.setComposerText)
  const attachments = useAppStore((state) => state.attachments)
  const removeAttachment = useAppStore((state) => state.removeAttachment)
  const mode = useAppStore((state) => state.mode)
  const setMode = useAppStore((state) => state.setMode)
  const provider = useAppStore((state) => state.provider)
  const model = useAppStore((state) => state.model)
  const setProviderModel = useAppStore((state) => state.setProviderModel)
  const sessionSearch = useAppStore((state) => state.sessionSearch)
  const setSessionSearch = useAppStore((state) => state.setSessionSearch)
  const sidePanel = useAppStore((state) => state.sidePanel)
  const setSidePanel = useAppStore((state) => state.setSidePanel)
  const selectedFile = useAppStore((state) => state.selectedFile)
  const clearSelectedFile = useAppStore((state) => state.clearSelectedFile)
  const sharedConversation = useAppStore((state) => state.sharedConversation)
  const setSharedConversation = useAppStore((state) => state.setSharedConversation)
  const clearSharedConversation = useAppStore((state) => state.clearSharedConversation)
  const startNewChat = useAppStore((state) => state.startNewChat)
  const renameCurrentSession = useAppStore((state) => state.renameCurrentSession)
  const activeModal = useAppStore((state) => state.activeModal)
  const modalPayload = useAppStore((state) => state.modalPayload)
  const openModal = useAppStore((state) => state.openModal)
  const closeModal = useAppStore((state) => state.closeModal)
  const addToast = useAppStore((state) => state.addToast)

  const deferredSearch = useDeferredValue(sessionSearch)
  const [renameDraft, setRenameDraft] = useState('')
  const [editDraft, setEditDraft] = useState('')

  const {
    messages,
    isLoadingHistory,
    isStreaming,
    currentMode,
    uploadFiles,
    sendMessage,
    selectSession,
    renameSession,
    deleteSession,
    submitFeedback,
    feedbackPending,
    askAboutFile,
    branchFromUserMessage,
    regenerateFromAssistant,
    exportChatAsMarkdown,
    exportChatAsPdf,
    shareConversation,
    toggleSpeech,
    speakingMessageId,
  } = useChat({ token, user })

  const { install, installState, platform, manualInstructions } = useInstallPrompt()
  const previewFile = useAppStore.getState().previewFile

  const modelsQuery = useQuery({
    queryKey: ['models'],
    queryFn: () => api.get('/models'),
    staleTime: 300_000,
  })

  const healthQuery = useQuery({
    queryKey: ['healthz-ready'],
    queryFn: () => api.get('/healthz/ready'),
    staleTime: 15_000,
    retry: 0,
    refetchInterval: 30_000,
  })

  const sessionsQuery = useQuery({
    queryKey: ['sessions', token || 'guest', deferredSearch],
    queryFn: () =>
      api.get(
        deferredSearch?.trim()
          ? `/search?query=${encodeURIComponent(deferredSearch.trim())}`
          : '/search',
        { token },
      ),
    enabled: isAuthenticated,
    placeholderData: (previous) => previous,
    staleTime: 15_000,
  })

  const insightsQuery = useQuery({
    queryKey: ['workspace-insights', token || 'guest'],
    queryFn: () => api.get('/insights/workspace', { token }),
    enabled: isAuthenticated,
    staleTime: 30_000,
    refetchInterval: 60_000,
  })

  const registry = modelsQuery.data?.providers || {}
  const sessions = sessionsQuery.data?.sessions || []

  useEffect(() => {
    const nextRegistry = modelsQuery.data?.providers
    if (!nextRegistry) return

    const defaultProvider = modelsQuery.data.default_provider
    const defaultModel = modelsQuery.data.default_model
    const enabledProviderIds = getEnabledProviderIds(nextRegistry)
    const resolvedProvider =
      nextRegistry[provider] && nextRegistry[provider].enabled !== false
        ? provider
        : enabledProviderIds[0] || defaultProvider
    const providerEntry = nextRegistry[resolvedProvider]
    const preferredModel = getPreferredModelId(
      resolvedProvider,
      providerEntry,
      defaultProvider,
      defaultModel,
    )

    if (!providerEntry) {
      setProviderModel(defaultProvider, defaultModel)
      return
    }

    const modelExists = providerEntry.models.some((entry) => entry.id === model)
    if (resolvedProvider !== provider || !modelExists) {
      setProviderModel(resolvedProvider, modelExists ? model : preferredModel)
    }
  }, [model, modelsQuery.data, provider, setProviderModel])

  useEffect(() => {
    if (activeModal === 'renameSession') {
      setRenameDraft(modalPayload?.title || '')
    }
    if (activeModal === 'editMessage') {
      setEditDraft(modalPayload?.content || '')
    }
  }, [activeModal, modalPayload])

  useEffect(() => {
    if (typeof window === 'undefined') return undefined

    const syncSharedConversation = () => {
      const payload = decodeSharePayload(window.location.hash)
      if (payload?.messages?.length) {
        setSharedConversation(payload)
        setSidePanel('analytics')
      } else if (window.location.hash.startsWith('#share=')) {
        clearSharedConversation()
      }
    }

    syncSharedConversation()
    window.addEventListener('hashchange', syncSharedConversation)
    return () => window.removeEventListener('hashchange', syncSharedConversation)
  }, [clearSharedConversation, setSharedConversation, setSidePanel])

  useEffect(() => {
    if (typeof window === 'undefined') return undefined
    const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
    const handleThemeChange = () => syncSystemTheme()
    mediaQuery.addEventListener?.('change', handleThemeChange)
    return () => mediaQuery.removeEventListener?.('change', handleThemeChange)
  }, [syncSystemTheme])

  const stats = useMemo(
    () => ({
      sessionCount: sessions.length,
      messageCount: messages.length,
      attachmentCount: attachments.length,
      modeLabel: currentMode.label,
      modeDescription: currentMode.description,
      totalSessions: insightsQuery.data?.total_sessions || 0,
      totalMessages: insightsQuery.data?.total_messages || 0,
      totalAttachments: insightsQuery.data?.total_attachments || 0,
      activeDays: insightsQuery.data?.active_days || 0,
      averageMessagesPerSession: insightsQuery.data?.average_messages_per_session || 0,
    }),
    [
      attachments.length,
      currentMode.description,
      currentMode.label,
      insightsQuery.data?.active_days,
      insightsQuery.data?.average_messages_per_session,
      insightsQuery.data?.total_attachments,
      insightsQuery.data?.total_messages,
      insightsQuery.data?.total_sessions,
      messages.length,
      sessions.length,
    ],
  )

  const pendingAuth = loginPending || registerPending

  const handleNewChat = () => {
    clearShareHash()
    clearSharedConversation()
    startNewChat()
    if (isSmallViewport()) {
      setSidebarOpen(false)
    }
  }

  const handleSelectSession = (session) => {
    clearShareHash()
    clearSharedConversation()
    selectSession(session)
    if (isSmallViewport()) {
      setSidebarOpen(false)
    }
  }

  const handleDeleteSessionRequest = (session) => {
    if (!session?.session_id) return
    openModal('deleteSession', {
      session_id: session.session_id,
      title: session.title || 'this chat',
    })
  }

  const handleSubmitMessage = async (text) => {
    await sendMessage({ text })
  }

  const handleCopyMessage = async (message) => {
    try {
      await navigator.clipboard.writeText(message.content || '')
      addToast({
        title: 'Message copied',
        description: 'The response is now in your clipboard.',
        variant: 'success',
      })
    } catch (error) {
      addToast({
        title: 'Copy failed',
        description: error.message || 'Clipboard access is unavailable.',
        variant: 'danger',
      })
    }
  }

  const handleOpenFeedback = () => {
    requireAuth(() => openModal('feedback'))
  }

  const handleShareConversation = async () => {
    if (!messages.length) {
      addToast({
        title: 'Nothing to share yet',
        description: 'Start a conversation first, then generate a shareable link.',
        variant: 'info',
      })
      return
    }
    await shareConversation()
  }

  const handleRenameSubmit = async () => {
    const title = renameDraft.trim()
    if (!title) return

    if (modalPayload?.session_id) {
      await renameSession({ sessionId: modalPayload.session_id, title })
    } else {
      renameCurrentSession(title)
    }
    closeModal()
  }

  const handleDeleteSubmit = async () => {
    if (!modalPayload?.session_id) return
    await deleteSession(modalPayload.session_id)
    closeModal()
  }

  const handleEditSubmit = async () => {
    const text = editDraft.trim()
    if (!text || !modalPayload) return
    await branchFromUserMessage(modalPayload, text)
    closeModal()
  }

  const handleClosePanel = () => {
    if (sidePanel === 'file') {
      clearSelectedFile()
      return
    }
    setSidePanel(null)
  }

  const handleInstallApp = async () => {
    const result = await install()

    if (result.status === 'accepted') {
      addToast({
        title: 'Install started',
        description: 'Confirm the browser prompt to finish installing OwnGPT.',
        variant: 'success',
      })
      return
    }

    if (result.status === 'dismissed') {
      addToast({
        title: 'Install dismissed',
        description: 'You can install OwnGPT anytime from the header button.',
        variant: 'info',
      })
      return
    }

    if (result.status === 'installed') {
      addToast({
        title: 'Already installed',
        description: 'OwnGPT is already available as an app on this device.',
        variant: 'info',
      })
      return
    }

    openModal('installHelp', result)
  }

  const activeProviderLabel = registry[provider]?.label || provider
  const appTitle = sharedConversation ? sharedConversation.title || 'Shared Conversation' : currentSessionTitle
  const runtimeStatus = healthQuery.data || {
    status: healthQuery.isError ? 'degraded' : 'checking',
    version: 'unknown',
    checks: {
      mongo: healthQuery.isError ? 'unreachable' : 'checking',
      vector_memory: 'unknown',
    },
    enabled_providers: getEnabledProviderIds(registry),
  }

  return (
    <div
      className={clsx(
        'relative flex h-screen overflow-hidden bg-background text-foreground transition-[padding] duration-300',
        sidebarOpen ? 'lg:pl-[288px]' : 'lg:pl-0',
      )}
    >
      <div className="pointer-events-none absolute inset-0 -z-10 overflow-hidden">
        <div className="app-background absolute inset-0" />
      </div>

      <Sidebar
        open={sidebarOpen}
        sessions={sessions}
        loading={sessionsQuery.isLoading && !sessions.length}
        currentSessionId={currentSessionId}
        searchValue={sessionSearch}
        onSearchChange={setSessionSearch}
        onSelectSession={handleSelectSession}
        onDeleteSession={handleDeleteSessionRequest}
        onNewSession={handleNewChat}
        onClose={() => setSidebarOpen(false)}
        stats={stats}
        insights={insightsQuery.data}
      />

      <div className="flex min-w-0 flex-1 flex-col">
        <AppHeader
          title={appTitle}
          theme={theme}
          themePreference={themePreference}
          currentSessionId={currentSessionId}
          canRenameSession={Boolean(user && !sharedConversation)}
          hasMessages={Boolean(messages.length)}
          onToggleSidebar={toggleSidebar}
          onThemePreferenceChange={setThemePreference}
          onNewChat={handleNewChat}
          onRenameSession={() =>
            openModal('renameSession', buildRenamePayload(currentSessionId, currentSessionTitle))
          }
          onDeleteSession={() =>
            currentSessionId
              ? openModal('deleteSession', {
                  session_id: currentSessionId,
                  title: currentSessionTitle,
                })
              : null
          }
          onShare={handleShareConversation}
          onExportMarkdown={exportChatAsMarkdown}
          onExportPdf={exportChatAsPdf}
          onToggleAnalytics={() => setSidePanel(sidePanel === 'analytics' ? null : 'analytics')}
          onFeedback={handleOpenFeedback}
          user={user}
          onLogin={() => openAuth('login')}
          onLogout={logout}
        />

        {sharedConversation ? (
          <div className="border-b border-border bg-background px-4 py-3 sm:px-6">
            <div className="mx-auto flex max-w-3xl flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
              <div className="flex items-center gap-2 text-sm font-medium text-foreground">
                <Link2 className="h-4 w-4 text-muted-foreground" />
                Shared conversation
              </div>
              <div className="flex flex-wrap items-center gap-2">
                <Button variant="secondary" onClick={handleNewChat}>
                  <Copy className="h-4 w-4" />
                  New chat
                </Button>
                <Button
                  variant="ghost"
                  onClick={() => {
                    clearShareHash()
                    clearSharedConversation()
                  }}
                >
                  <X className="h-4 w-4" />
                  Dismiss
                </Button>
              </div>
            </div>
          </div>
        ) : null}

        <div className="flex min-h-0 flex-1">
          <div className="flex min-w-0 flex-1 flex-col">
            <ChatArea
              messages={messages}
              loading={isLoadingHistory}
              user={user}
              onCopyMessage={handleCopyMessage}
              onRegenerate={regenerateFromAssistant}
              onEdit={(message) => openModal('editMessage', message)}
              onSpeak={toggleSpeech}
              onPreviewFile={previewFile}
              speakingMessageId={speakingMessageId}
            />

            <Composer
              text={composerText}
              onTextChange={setComposerText}
              attachments={attachments}
              onRemoveAttachment={removeAttachment}
              onUpload={uploadFiles}
              onSend={handleSubmitMessage}
              onAskAboutFile={askAboutFile}
              onPreviewFile={previewFile}
              mode={mode}
              onModeChange={setMode}
              provider={activeProviderLabel}
              model={model}
              onOpenModels={() => openModal('model')}
              disabled={!isAuthenticated}
              streaming={isStreaming}
              onRequireAuth={() => openAuth('login')}
            />
          </div>

          <AnimatePresence initial={false}>
            <WorkspacePanel
              sidePanel={sidePanel}
              selectedFile={selectedFile}
              stats={stats}
              insights={insightsQuery.data}
              isAuthenticated={isAuthenticated}
              runtimeStatus={runtimeStatus}
              activeProvider={activeProviderLabel}
              activeModel={model}
              onClose={handleClosePanel}
              onPreviewAnalytics={() => setSidePanel('analytics')}
              onAskAboutFile={askAboutFile}
            />
          </AnimatePresence>
        </div>
      </div>

      <AuthDialog
        open={isAuthDialogOpen}
        mode={authMode || 'login'}
        onModeChange={openAuth}
        onClose={closeAuth}
        onLogin={login}
        onRegister={register}
        onGoogleLogin={loginWithGoogle}
        pending={pendingAuth}
      />

      <FeedbackDialog
        open={activeModal === 'feedback'}
        onOpenChange={(next) => {
          if (!next) closeModal()
        }}
        onSubmit={submitFeedback}
        pending={feedbackPending}
      />

      <ModelDialog
        open={activeModal === 'model'}
        onOpenChange={(next) => {
          if (!next) closeModal()
        }}
        registry={registry}
        currentProvider={provider}
        currentModel={model}
        onSelect={setProviderModel}
      />

      <ConfirmDialog
        open={activeModal === 'deleteSession'}
        onOpenChange={(next) => {
          if (!next) closeModal()
        }}
        title="Delete conversation?"
        description={
          modalPayload?.title
            ? `This will permanently remove "${modalPayload.title}" and its saved messages.`
            : 'This will permanently remove the selected conversation and its saved messages.'
        }
        onConfirm={handleDeleteSubmit}
        confirmLabel="Delete"
        destructive
      />

      <Modal
        open={activeModal === 'renameSession'}
        onOpenChange={(next) => {
          if (!next) closeModal()
        }}
        size="sm"
        title="Rename conversation"
        footer={
          <div className="flex justify-end gap-2">
            <Button variant="ghost" onClick={closeModal}>
              Cancel
            </Button>
            <Button variant="primary" onClick={handleRenameSubmit} disabled={!renameDraft.trim()}>
              Save title
            </Button>
          </div>
        }
      >
        <label className="block space-y-2">
          <span className="text-sm font-medium text-foreground">Conversation title</span>
          <div className="field rounded-lg px-4 py-3">
            <input
              value={renameDraft}
              onChange={(event) => setRenameDraft(event.target.value)}
              placeholder="Product launch planning"
              className="w-full bg-transparent text-sm text-foreground outline-none placeholder:text-muted-foreground"
            />
          </div>
        </label>
      </Modal>

      <Modal
        open={activeModal === 'editMessage'}
        onOpenChange={(next) => {
          if (!next) closeModal()
        }}
        size="md"
        title="Edit user prompt"
        footer={
          <div className="flex justify-end gap-2">
            <Button variant="ghost" onClick={closeModal}>
              Cancel
            </Button>
            <Button variant="primary" onClick={handleEditSubmit} disabled={!editDraft.trim()}>
              <Sparkles className="h-4 w-4" />
              Run new branch
            </Button>
          </div>
        }
      >
        <label className="block space-y-2">
          <span className="text-sm font-medium text-foreground">Prompt</span>
          <div className="field rounded-lg px-4 py-3">
            <textarea
              value={editDraft}
              onChange={(event) => setEditDraft(event.target.value)}
              className="min-h-[220px] w-full bg-transparent text-sm leading-7 text-foreground outline-none placeholder:text-muted-foreground"
              placeholder="Refine the original message and branch from it..."
            />
          </div>
        </label>
      </Modal>

      <Modal
        open={activeModal === 'installHelp'}
        onOpenChange={(next) => {
          if (!next) closeModal()
        }}
        size="sm"
        title="Install OwnGPT"
        footer={
          <div className="flex justify-end gap-2">
            <Button variant="ghost" onClick={closeModal}>
              Close
            </Button>
            <Button variant="primary" onClick={closeModal}>
              <Download className="h-4 w-4" />
              I understand
            </Button>
          </div>
        }
      >
        <div className="space-y-4">
          <div className="surface-soft p-4">
            <p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted-foreground">
              Recommended steps
            </p>
            <div className="mt-3 space-y-3 text-sm leading-6 text-foreground">
              {getInstallSteps(modalPayload?.platform || platform, modalPayload?.instructions || manualInstructions).map((step, index) => (
                <div key={step} className="flex items-start gap-3">
                  <span className="mt-0.5 inline-flex h-6 w-6 items-center justify-center rounded-lg bg-accent/10 text-xs font-semibold text-accent">
                    {index + 1}
                  </span>
                  <span>{step}</span>
                </div>
              ))}
            </div>
          </div>
          <div className="rounded-lg border border-border bg-background px-4 py-4 text-sm leading-6 text-muted-foreground">
            {modalPayload?.instructions || manualInstructions}
          </div>
        </div>
      </Modal>

      <Toaster />
    </div>
  )
}
