import { useCallback, useEffect, useMemo, useRef } from 'react';
import { createPortal } from 'react-dom';
import { AnimatePresence, motion } from 'framer-motion';
import { useNavigate, useRouterState } from '@tanstack/react-router';
import { X, ArrowUp } from 'lucide-react';
import {
  useFloatingChat,
  computeDualAnchor,
  CHAT_WIDTH,
  CHAT_HEIGHT,
  PADDING,
} from '@/context/FloatingChatContext';
import { useAIChat, type ChatContext } from '@/hooks/useAIChat';
import { ChatMarkdown } from '@/components/ai/AIChatPanel';
import { Skeleton } from '@/components/ui/skeleton';
import { trpc } from '@/lib/trpc';

/** Map section IDs to their routes for cross-page navigation */
const SECTION_ROUTES: Record<string, string> = {
  'project-summary': 'project',
  'project-timeline': 'project',
  'project-tasks': 'project',
  'project-notes': 'project',
  'tasks-overview': '/tasks',
  'tasks-list': '/tasks',
  'timeline-chart': '/timeline',
  'note-editor': 'note',
};

function FloatingChatInner() {
  const { state, sections, close, setMorphTarget, moveToSection, updatePosition, setPendingSection } = useFloatingChat();
  const utils = trpc.useUtils();
  const navigate = useNavigate();
  const routerState = useRouterState();
  const prevPathRef = useRef(routerState.location.pathname);

  // Active section lookup
  const activeSection = sections.get(state.activeSectionId ?? '');

  // Chat context derived from active section
  const chatContext = useMemo<ChatContext>(
    () => ({
      type: activeSection?.projectId ? 'project' : 'global',
      projectId: activeSection?.projectId,
      uiContext: activeSection?.label,
    }),
    [activeSection?.projectId, activeSection?.label],
  );

  // Handle [SECTION:xxx] tags from AI responses
  const handleSectionTag = useCallback((sectionId: string) => {
    // Same-page: section is already registered
    const targetSection = sections.get(sectionId);
    if (targetSection) {
      moveToSection(sectionId);
      targetSection.ref.current?.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
      return;
    }

    // Cross-page: section not registered, navigate to its route
    const route = SECTION_ROUTES[sectionId];
    if (!route) return;

    setPendingSection({ sectionId });

    if (route === 'project' && state.projectId) {
      // Navigate to the project page (stay on same project)
      // Project sections re-register on mount and pendingSection will auto-open
      void navigate({ to: '/projects/$projectId', params: { projectId: state.projectId } });
    } else if (route.startsWith('/')) {
      void navigate({ to: route });
    }
    // 'note' type requires noteId — skip cross-page for now
  }, [sections, moveToSection, setPendingSection, state.projectId, navigate]);

  const {
    messages,
    input,
    setInput,
    isStreaming,
    streamingContent,
    handleSend,
    clearMessages,
  } = useAIChat(chatContext, { onSectionTag: handleSectionTag });

  const containerRef = useRef<HTMLDivElement>(null);

  // ---- Close on Escape ----

  useEffect(() => {
    if (!state.isOpen) return;
    const handler = (e: KeyboardEvent) => {
      if (e.key === 'Escape') {
        e.stopPropagation();
        close();
      }
    };
    document.addEventListener('keydown', handler);
    return () => document.removeEventListener('keydown', handler);
  }, [state.isOpen, close]);

  // ---- Close on route change (unless cross-page navigation pending) ----

  useEffect(() => {
    const currentPath = routerState.location.pathname;
    if (prevPathRef.current !== currentPath && state.isOpen && !state.pendingSection) {
      close();
    }
    prevPathRef.current = currentPath;
  }, [routerState.location.pathname, state.isOpen, state.pendingSection, close]);

  // ---- Clear messages on close ----

  const prevOpenRef = useRef(state.isOpen);
  useEffect(() => {
    if (prevOpenRef.current && !state.isOpen) {
      clearMessages();
    }
    prevOpenRef.current = state.isOpen;
  }, [state.isOpen, clearMessages]);

  // ---- AI action: morph into newly-created task ----

  useEffect(() => {
    if (!state.isOpen) return;

    const unsubscribe = window.electronAI.onAction((action) => {
      if (action.type === 'task_created' && action.taskId) {
        // Invalidate task queries so the new TaskRow renders
        void utils.tasks.list.invalidate();

        // Set the morph target layoutId
        setMorphTarget(`task-morph-${action.taskId}`);

        // Wait for the TaskRow to render, then close (triggering FLIP)
        requestAnimationFrame(() => {
          requestAnimationFrame(() => {
            close();
          });
        });
      }
    });

    return unsubscribe;
  }, [state.isOpen, utils, setMorphTarget, close]);

  // ---- Window resize: keep within bounds ----

  useEffect(() => {
    if (!state.isOpen) return;
    const handler = () => {
      // Re-anchor if the container would go offscreen
      const el = containerRef.current;
      if (el) {
        const rect = el.getBoundingClientRect();
        if (rect.right > window.innerWidth || rect.bottom > window.innerHeight) {
          el.style.left = `${Math.max(PADDING, Math.min(state.position.x, window.innerWidth - CHAT_WIDTH - PADDING))}px`;
          el.style.top = `${Math.max(PADDING, Math.min(state.position.y, window.innerHeight - CHAT_HEIGHT - PADDING))}px`;
        }
      }
    };
    window.addEventListener('resize', handler);
    return () => window.removeEventListener('resize', handler);
  }, [state.isOpen, state.position.x, state.position.y]);

  // ---- Scroll tracking: dual-anchor repositioning ----

  useEffect(() => {
    if (!state.isOpen || !state.activeSectionId) return;
    const section = sections.get(state.activeSectionId);
    if (!section || section.anchorMode === 'right-margin') return;

    const el = section.ref.current;
    if (!el) return;

    // Find scrollable ancestor
    let scrollParent: HTMLElement | null = el.parentElement;
    while (scrollParent) {
      const style = getComputedStyle(scrollParent);
      if (style.overflow === 'auto' || style.overflow === 'scroll' ||
          style.overflowY === 'auto' || style.overflowY === 'scroll') {
        break;
      }
      // Also check for Radix ScrollArea viewport
      if (scrollParent.hasAttribute('data-radix-scroll-area-viewport')) break;
      scrollParent = scrollParent.parentElement;
    }

    if (!scrollParent) return;

    let rafId: number | null = null;
    const handleScroll = () => {
      if (rafId !== null) return;
      rafId = requestAnimationFrame(() => {
        rafId = null;
        const newPos = computeDualAnchor(section);
        if (newPos) {
          updatePosition(newPos);
        }
        // null = fully off-screen → freeze (do nothing)
      });
    };

    scrollParent.addEventListener('scroll', handleScroll, { passive: true });
    return () => {
      scrollParent.removeEventListener('scroll', handleScroll);
      if (rafId !== null) cancelAnimationFrame(rafId);
    };
  }, [state.isOpen, state.activeSectionId, sections, updatePosition]);

  // ---- Auto-scroll messages ----

  const scrollRef = useRef<HTMLDivElement>(null);
  const scrollToBottom = useCallback(() => {
    const el = scrollRef.current;
    if (el) el.scrollTo({ top: el.scrollHeight });
  }, []);

  useEffect(() => {
    scrollToBottom();
  }, [messages, streamingContent, scrollToBottom]);

  // ---- Auto-focus input on open ----

  const inputRef = useRef<HTMLTextAreaElement>(null);
  useEffect(() => {
    if (state.isOpen) {
      const timer = setTimeout(() => inputRef.current?.focus(), 100);
      return () => clearTimeout(timer);
    }
  }, [state.isOpen]);

  // ---- Input handling ----

  const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
    if (e.key === 'Enter' && !e.shiftKey) {
      e.preventDefault();
      handleSend();
    }
  };

  const hasMessages = messages.length > 0 || isStreaming;

  return (
    <AnimatePresence>
      {state.isOpen && (
        <motion.div
          ref={containerRef}
          key="floating-chat"
          layout
          layoutId={state.morphTargetId ?? undefined}
          initial={{ opacity: 0, scale: 0.95, y: 12 }}
          animate={{ opacity: 1, scale: 1, y: 0 }}
          exit={{ opacity: 0, scale: 0.95, y: 12 }}
          transition={{ type: 'spring', stiffness: 400, damping: 30 }}
          style={{
            position: 'fixed',
            left: state.position.x,
            top: state.position.y,
            width: state.position.width,
            zIndex: 9999,
          }}
          className="flex flex-col gap-2"
        >
          {/* ---- Messages panel (appears when chat has content) ---- */}
          <AnimatePresence>
            {hasMessages && (
              <motion.div
                key="messages-panel"
                initial={{ opacity: 0, height: 0, scale: 0.97 }}
                animate={{ opacity: 1, height: 'auto', scale: 1 }}
                exit={{ opacity: 0, height: 0, scale: 0.97 }}
                transition={{ type: 'spring', stiffness: 400, damping: 30 }}
                className="rounded-2xl"
              >
                <div
                  ref={scrollRef}
                  className="max-h-[300px] overflow-y-auto rounded-2xl [&::-webkit-scrollbar]:w-2.5 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-border"
                >
                  <div className="flex flex-col gap-2.5 p-3">
                    {messages.map((msg) => {
                      if (msg.role === 'user') {
                        return (
                          <div key={msg.id} className="flex justify-end">
                            <div className="max-w-[80%] rounded-2xl rounded-br-md bg-accent text-primary-foreground px-3.5 py-2 shadow-sm">
                              <p className="text-xs whitespace-pre-wrap leading-relaxed">
                                {msg.content}
                              </p>
                            </div>
                          </div>
                        );
                      }

                      if (msg.error) {
                        return (
                          <div key={msg.id} className="flex justify-start">
                            <div className="max-w-[80%] rounded-2xl rounded-bl-md bg-destructive/10 px-3.5 py-2">
                              <p className="text-xs text-destructive whitespace-pre-wrap leading-relaxed">
                                {msg.content}
                              </p>
                            </div>
                          </div>
                        );
                      }

                      return (
                        <div key={msg.id} className="flex justify-start">
                          <div className="max-w-[80%] rounded-2xl rounded-bl-md bg-primary text-primary-foreground px-3.5 py-2">
                            <div className="text-xs">
                              <ChatMarkdown content={msg.content} />
                            </div>
                          </div>
                        </div>
                      );
                    })}

                    {/* Streaming */}
                    {isStreaming && (
                      <div className="flex justify-start">
                        <div className="max-w-[80%] rounded-2xl rounded-bl-md bg-primary text-primary-foreground px-3.5 py-2">
                          {streamingContent ? (
                            <div className="text-xs">
                              <ChatMarkdown content={streamingContent} />
                            </div>
                          ) : (
                            <div className="space-y-1.5 py-0.5">
                              <Skeleton className="h-3 w-36" />
                              <Skeleton className="h-3 w-24" />
                            </div>
                          )}
                        </div>
                      </div>
                    )}
                  </div>
                </div>
              </motion.div>
            )}
          </AnimatePresence>

          {/* ---- Floating input bar ---- */}
          <div className="relative rounded-2xl bg-background/80 backdrop-blur-2xl shadow-[0_8px_60px_-12px_rgba(0,0,0,0.5)] border border-border/30 ring-1 ring-white/5 transition-shadow focus-within:shadow-[0_8px_60px_-8px_rgba(0,0,0,0.7)] focus-within:ring-ring/20">
            {/* Close button */}
            <button
              onClick={close}
              className="absolute -top-1.5 -right-1.5 flex h-5 w-5 items-center justify-center rounded-full bg-muted/90 backdrop-blur-sm border border-border/50 shadow-sm hover:bg-muted transition-colors z-10"
            >
              <X size={10} />
            </button>

            <div className="flex items-center gap-2 px-3 py-2.5">
              <textarea
                ref={inputRef}
                value={input}
                onChange={(e) => setInput(e.target.value)}
                onKeyDown={handleKeyDown}
                placeholder={`Ask about ${activeSection?.label ?? 'this section'}...`}
                rows={1}
                className="flex-1 resize-none bg-transparent text-sm placeholder:text-muted-foreground/60 outline-none max-h-20 overflow-y-auto"
                style={{ fieldSizing: 'content' } as React.CSSProperties}
              />
              <button
                onClick={() => handleSend()}
                disabled={!input.trim() || isStreaming}
                className="flex h-7 w-7 shrink-0 items-center justify-center rounded-xl bg-primary text-primary-foreground shadow-sm transition-all hover:bg-primary/90 active:scale-95 disabled:opacity-30 disabled:cursor-not-allowed"
              >
                <ArrowUp size={14} />
              </button>
            </div>
          </div>
        </motion.div>
      )}
    </AnimatePresence>
  );
}

export function FloatingChatPortal() {
  return createPortal(<FloatingChatInner />, document.body);
}
