import {
  createContext,
  useContext,
  useCallback,
  useEffect,
  useState,
  useRef,
  type ReactNode,
  type RefObject,
} from 'react';

// ---------- Types ----------

export interface AISection {
  id: string; // e.g. "project-tasks", "tasks-list", "timeline-chart"
  label: string; // Human-readable, e.g. "Tasks", "Project Timeline"
  ref: RefObject<HTMLElement | null>;
  projectId?: string; // If section is project-scoped
}

interface FloatingChatState {
  isOpen: boolean;
  activeSectionId: string | null;
  position: { x: number; y: number; width: number };
  morphTargetId: string | null;
  projectId?: string;
}

interface FloatingChatContextValue {
  // State
  state: FloatingChatState;
  sections: Map<string, AISection>;

  // Section registry
  registerSection: (section: AISection) => void;
  unregisterSection: (id: string) => void;

  // Actions
  openAtSection: (sectionId: string) => void;
  moveToSection: (sectionId: string) => void;
  close: () => void;
  setMorphTarget: (id: string | null) => void;
}

// ---------- Constants ----------

const CHAT_WIDTH = 380;
const CHAT_HEIGHT = 420;
const PADDING = 16;

// ---------- Position computation ----------

function computeAnchorPosition(
  sectionRef: RefObject<HTMLElement | null>,
): { x: number; y: number; width: number } {
  const el = sectionRef.current;
  if (!el) return { x: PADDING, y: PADDING, width: CHAT_WIDTH };

  const rect = el.getBoundingClientRect();

  // Anchor to top-right of section, offset inward
  let x = rect.right - CHAT_WIDTH - PADDING;
  let y = rect.top + PADDING;

  // Edge-collision clamping
  x = Math.max(PADDING, Math.min(x, window.innerWidth - CHAT_WIDTH - PADDING));
  y = Math.max(
    PADDING,
    Math.min(y, window.innerHeight - CHAT_HEIGHT - PADDING),
  );

  return { x, y, width: CHAT_WIDTH };
}

// ---------- Context ----------

const FloatingChatCtx = createContext<FloatingChatContextValue | null>(null);

export function useFloatingChat(): FloatingChatContextValue {
  const ctx = useContext(FloatingChatCtx);
  if (!ctx)
    throw new Error('useFloatingChat must be used within FloatingChatProvider');
  return ctx;
}

// Convenience hook for pages to register a section
export function useAISection(section: AISection): void {
  const { registerSection, unregisterSection } = useFloatingChat();

  useEffect(() => {
    registerSection(section);
    return () => unregisterSection(section.id);
  }, [section.id, registerSection, unregisterSection]);
}

// ---------- Provider ----------

export function FloatingChatProvider({ children }: { children: ReactNode }) {
  const sectionsRef = useRef<Map<string, AISection>>(new Map());
  const [sections, setSections] = useState<Map<string, AISection>>(new Map());
  const [state, setState] = useState<FloatingChatState>({
    isOpen: false,
    activeSectionId: null,
    position: { x: 0, y: 0, width: CHAT_WIDTH },
    morphTargetId: null,
  });

  const registerSection = useCallback((section: AISection) => {
    sectionsRef.current.set(section.id, section);
    setSections(new Map(sectionsRef.current));
  }, []);

  const unregisterSection = useCallback((id: string) => {
    sectionsRef.current.delete(id);
    setSections(new Map(sectionsRef.current));
  }, []);

  const openAtSection = useCallback((sectionId: string) => {
    const section = sectionsRef.current.get(sectionId);
    if (!section) return;

    const position = computeAnchorPosition(section.ref);

    setState({
      isOpen: true,
      activeSectionId: sectionId,
      position,
      morphTargetId: null,
      projectId: section.projectId,
    });
  }, []);

  const moveToSection = useCallback((sectionId: string) => {
    const section = sectionsRef.current.get(sectionId);
    if (!section) return;

    const position = computeAnchorPosition(section.ref);

    setState((prev) => ({
      ...prev,
      activeSectionId: sectionId,
      position,
      projectId: section.projectId,
    }));
  }, []);

  const close = useCallback(() => {
    setState((prev) => ({
      ...prev,
      isOpen: false,
      activeSectionId: null,
      morphTargetId: null,
    }));
  }, []);

  const setMorphTarget = useCallback((id: string | null) => {
    setState((prev) => ({ ...prev, morphTargetId: id }));
  }, []);

  return (
    <FloatingChatCtx.Provider
      value={{
        state,
        sections,
        registerSection,
        unregisterSection,
        openAtSection,
        moveToSection,
        close,
        setMorphTarget,
      }}
    >
      {children}
    </FloatingChatCtx.Provider>
  );
}
