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

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

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
  anchorMode?: 'top-right' | 'right-margin'; // default: 'top-right'
}

interface SectionOpenOpts {
  clickY?: number; // For right-margin mode: Y-coordinate of the double-click
}

interface FloatingChatState {
  isOpen: boolean;
  activeSectionId: string | null;
  position: { x: number; y: number; width: number };
  morphTargetId: string | null;
  projectId?: string;
  pendingSection?: { sectionId: string; clickY?: number }; // For cross-page navigation
}

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

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

  // Actions
  openAtSection: (sectionId: string, opts?: SectionOpenOpts) => void;
  moveToSection: (sectionId: string, opts?: SectionOpenOpts) => void;
  close: () => void;
  setMorphTarget: (id: string | null) => void;
  updatePosition: (pos: { x: number; y: number; width: number }) => void;
  setPendingSection: (pending: { sectionId: string; clickY?: number } | undefined) => void;
}

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

/** Dynamic chat width: 35% of viewport, clamped between 320px and 520px. */
export function getChatWidth(): number {
  return Math.min(630, Math.max(320, Math.round(window.innerWidth * 0.35)));
}

export const CHAT_HEIGHT = 420;
export const PADDING = 16;

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

function clampPosition(x: number, y: number): { x: number; y: number } {
  const w = getChatWidth();
  return {
    x: Math.max(PADDING, Math.min(x, window.innerWidth - w - PADDING)),
    y: Math.max(PADDING, Math.min(y, window.innerHeight - CHAT_HEIGHT - PADDING)),
  };
}

function computeAnchorPosition(
  section: AISection,
  opts?: SectionOpenOpts,
): { x: number; y: number; width: number } {
  const el = section.ref.current;
  const w = getChatWidth();
  if (!el) return { x: PADDING, y: PADDING, width: w };

  const rect = el.getBoundingClientRect();
  const mode = section.anchorMode ?? 'top-right';

  if (mode === 'right-margin') {
    // Position to the right of the section at the click Y-coordinate
    const rawX = rect.right + PADDING;
    const rawY = opts?.clickY ?? rect.top + PADDING;
    const { x, y } = clampPosition(rawX, rawY);
    return { x, y, width: w };
  }

  // Default: top-right of section
  const rawX = rect.right - w - PADDING;
  const rawY = rect.top + PADDING;
  const { x, y } = clampPosition(rawX, rawY);
  return { x, y, width: w };
}

/**
 * Dual-anchor recomputation for scroll tracking.
 * Returns null when the section is fully off-screen (freeze at last position).
 */
export function computeDualAnchor(
  section: AISection,
): { x: number; y: number; width: number } | null {
  const el = section.ref.current;
  if (!el) return null;

  // Skip scroll tracking for right-margin mode (stays at fixed clickY)
  if (section.anchorMode === 'right-margin') return null;

  const rect = el.getBoundingClientRect();
  const w = getChatWidth();

  // Fully off-screen — freeze
  if (rect.bottom < 0 || rect.top > window.innerHeight) return null;

  // Primary anchor: top-right (when section top is visible)
  if (rect.top >= PADDING) {
    const { x, y } = clampPosition(
      rect.right - w - PADDING,
      rect.top + PADDING,
    );
    return { x, y, width: w };
  }

  // Fallback anchor: bottom-right (when section top scrolled off)
  if (rect.bottom > CHAT_HEIGHT) {
    const { x, y } = clampPosition(
      rect.right - w - PADDING,
      rect.bottom - CHAT_HEIGHT - PADDING,
    );
    return { x, y, width: w };
  }

  // Section visible but too small for fallback — clamp to top
  const { x, y } = clampPosition(
    rect.right - w - PADDING,
    PADDING,
  );
  return { x, y, width: w };
}

// ---------- 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;
}


// ---------- 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: getChatWidth() },
    morphTargetId: null,
  });

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

    // Check if there's a pending section to open after cross-page navigation
    setState((prev) => {
      if (prev.pendingSection && prev.pendingSection.sectionId === section.id) {
        const position = computeAnchorPosition(section, { clickY: prev.pendingSection.clickY });
        return {
          ...prev,
          isOpen: true,
          activeSectionId: section.id,
          position,
          morphTargetId: null,
          projectId: section.projectId,
          pendingSection: undefined,
        };
      }
      return prev;
    });
  }, []);

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

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

    const position = computeAnchorPosition(section, opts);

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

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

    const position = computeAnchorPosition(section, opts);

    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 }));
  }, []);

  const updatePosition = useCallback((pos: { x: number; y: number; width: number }) => {
    setState((prev) => ({ ...prev, position: pos }));
  }, []);

  const setPendingSection = useCallback((pending: { sectionId: string; clickY?: number } | undefined) => {
    setState((prev) => ({ ...prev, pendingSection: pending }));
  }, []);

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