'use client';

import { useEffect, useState, useRef, useCallback } from 'react';
import { IChartApi, ISeriesApi, Time, MouseEventParams } from 'lightweight-charts';
import { SpanRectanglePrimitive, SpanData } from './SpanRectanglePrimitive';
import SpanPopover from './SpanPopover';
import type { Candle, SpanAnnotation, SpanLabelType } from '@/types';

// Candle, SpanAnnotation, SpanLabelType are imported from @/types above.

interface SpanAnnotationManagerProps {
  chart: IChartApi | null;
  series: ISeriesApi<'Candlestick'> | null;
  activeTool: string | null;
  candles: Candle[];
  spanAnnotations: SpanAnnotation[];
  spanLabelTypes: SpanLabelType[];
  selectedSpanId: number | null;
  onSpanAnnotationsChange: () => void;
  onSelectedSpanChange: (spanId: number | null) => void;
  activeChartId: number | null;
}

type InteractionState = 'idle' | 'first-click-done' | 'popover-open';

export default function SpanAnnotationManager({
  chart,
  series,
  activeTool,
  candles,
  spanAnnotations,
  spanLabelTypes,
  selectedSpanId,
  onSpanAnnotationsChange,
  onSelectedSpanChange,
  activeChartId,
}: SpanAnnotationManagerProps) {
  const [interactionState, setInteractionState] = useState<InteractionState>('idle');
  const [startCandle, setStartCandle] = useState<Candle | null>(null);
  const [endCandle, setEndCandle] = useState<Candle | null>(null);
  const [cursorCandle, setCursorCandle] = useState<Candle | null>(null);
  const previewPrimitiveRef = useRef<SpanRectanglePrimitive | null>(null);
  const [popoverOpen, setPopoverOpen] = useState(false);
  const [editingSpan, setEditingSpan] = useState<SpanAnnotation | null>(null);
  const primitivesRef = useRef<Map<number, SpanRectanglePrimitive>>(new Map());
  const selectedSpanIdRef = useRef<number | null>(selectedSpanId);
  const hasInitializedRef = useRef(false);

  // Keep selectedSpanIdRef in sync with prop
  useEffect(() => {
    selectedSpanIdRef.current = selectedSpanId;
  }, [selectedSpanId]);

  // Cleanup preview primitive on unmount
  useEffect(() => {
    return () => {
      if (previewPrimitiveRef.current && series) {
        try {
          series.detachPrimitive(previewPrimitiveRef.current);
        } catch {
          // series may already be disposed
        }
        previewPrimitiveRef.current = null;
      }
    };
  }, [series]);

  // Find nearest candle to a timestamp
  const findNearestCandle = (timestamp: number): Candle | null => {
    if (candles.length === 0) return null;
    return candles.reduce((prev, curr) => {
      return Math.abs(curr.time - timestamp) < Math.abs(prev.time - timestamp) ? curr : prev;
    });
  };

  // Calculate price range for candles in a span
  const calculatePriceRange = (start: Candle, end: Candle): { max_high: number; min_low: number } => {
    const startIdx = candles.findIndex((c) => c.time === start.time);
    const endIdx = candles.findIndex((c) => c.time === end.time);

    if (startIdx === -1 || endIdx === -1) {
      return { max_high: Math.max(start.high, end.high), min_low: Math.min(start.low, end.low) };
    }

    const [minIdx, maxIdx] = [Math.min(startIdx, endIdx), Math.max(startIdx, endIdx)];
    const spanCandles = candles.slice(minIdx, maxIdx + 1);

    const max_high = Math.max(...spanCandles.map((c) => c.high));
    const min_low = Math.min(...spanCandles.map((c) => c.low));

    return { max_high, min_low };
  };

  // Full reconciliation: rebuild all primitives when annotation list changes
  useEffect(() => {
    if (!series || !chart) return;

    // Clear existing primitives
    primitivesRef.current.forEach((primitive) => {
      series.detachPrimitive(primitive);
    });
    primitivesRef.current.clear();

    // Create primitives for each span annotation
    spanAnnotations.forEach((span) => {
      // Find candles within span time range for price calculation
      const spanCandles = candles.filter(
        (c) => c.time >= span.start_time && c.time <= span.end_time
      );
      let max_high: number;
      let min_low: number;
      if (spanCandles.length > 0) {
        max_high = Math.max(...spanCandles.map((c) => c.high));
        min_low = Math.min(...spanCandles.map((c) => c.low));
      } else {
        // Fallback: find nearest candles to span boundaries
        const nearest = candles.reduce(
          (acc, c) => {
            const distStart = Math.abs(c.time - span.start_time);
            const distEnd = Math.abs(c.time - span.end_time);
            if (distStart < acc.startDist) acc = { ...acc, startCandle: c, startDist: distStart };
            if (distEnd < acc.endDist) acc = { ...acc, endCandle: c, endDist: distEnd };
            return acc;
          },
          { startCandle: candles[0], startDist: Infinity, endCandle: candles[0], endDist: Infinity }
        );
        max_high = Math.max(nearest.startCandle?.high ?? 0, nearest.endCandle?.high ?? 0);
        min_low = Math.min(nearest.startCandle?.low ?? 0, nearest.endCandle?.low ?? 0);
      }

      const spanData: SpanData = {
        id: span.id,
        start_time: span.start_time,
        end_time: span.end_time,
        label: span.label,
        color: span.color,
        max_high: max_high,
        min_low: min_low,
      };

      const primitive = new SpanRectanglePrimitive({
        data: spanData,
        isSelected: span.id === selectedSpanIdRef.current,
      });

      series.attachPrimitive(primitive);
      primitivesRef.current.set(span.id, primitive);
    });
  }, [spanAnnotations, series, chart, candles]);

  // Selection-only effect: update visual state without rebuilding primitives
  useEffect(() => {
    primitivesRef.current.forEach((primitive, spanId) => {
      primitive.setSelected(spanId === selectedSpanId);
    });
  }, [selectedSpanId]);

  // Fit chart content only on initial load when chart and series become available
  useEffect(() => {
    if (!chart || !series) return;
    if (hasInitializedRef.current) return;
    hasInitializedRef.current = true;
    chart.timeScale().fitContent();
  }, [chart, series]);

  // Handle clicks on chart for span tool and span selection
  useEffect(() => {
    if (!chart || !series) return;

    // Clean up preview if tool changes away from span
    if (activeTool !== 'span' && previewPrimitiveRef.current) {
      series.detachPrimitive(previewPrimitiveRef.current);
      previewPrimitiveRef.current = null;
      setInteractionState('idle');
      setStartCandle(null);
      setEndCandle(null);
    }

    const handleClick = (param: MouseEventParams) => {
      if (!param.point) return;
      const point = param.point;

      const time = chart.timeScale().coordinateToTime(point.x);
      const price = series.coordinateToPrice(point.y);
      if (!time || !price) return;

      // Handle span tool two-click interaction
      if (activeTool === 'span') {
        const timestamp = typeof time === 'string' ? Date.parse(time) / 1000 : (time as number);
        const nearestCandle = findNearestCandle(timestamp);

        if (!nearestCandle) return;

        if (interactionState === 'idle') {
          // Check if clicking on existing span for selection
          let clickedSpanId: number | null = null;
          primitivesRef.current.forEach((primitive, spanId) => {
            if (primitive.hitTest(point.x, point.y)) {
              clickedSpanId = spanId;
            }
          });

          if (clickedSpanId !== null) {
            // Select/deselect span
            if (clickedSpanId === selectedSpanId) {
              onSelectedSpanChange(null);
            } else {
              onSelectedSpanChange(clickedSpanId);
            }
            return;
          }

          // First click: set start candle
          setStartCandle(nearestCandle);
          setInteractionState('first-click-done');
        } else if (interactionState === 'first-click-done') {
          // Second click: set end candle and open popover
          setEndCandle(nearestCandle);
          setInteractionState('popover-open');
          setPopoverOpen(true);

          // Clean up preview primitive
          if (previewPrimitiveRef.current) {
            series.detachPrimitive(previewPrimitiveRef.current);
            previewPrimitiveRef.current = null;
          }
        }
      } else if (activeTool === 'delete') {
        // Delete span on click with delete tool
        let clickedSpanId: number | null = null;
        primitivesRef.current.forEach((primitive, spanId) => {
          if (primitive.hitTest(point.x, point.y)) {
            clickedSpanId = spanId;
          }
        });

        if (clickedSpanId !== null) {
          handleDeleteSpan(clickedSpanId);
        }
      } else if (!activeTool) {
        // Click to select span when no tool is active
        let clickedSpanId: number | null = null;
        primitivesRef.current.forEach((primitive, spanId) => {
          if (primitive.hitTest(point.x, point.y)) {
            clickedSpanId = spanId;
          }
        });

        if (clickedSpanId !== null) {
          if (clickedSpanId === selectedSpanId) {
            onSelectedSpanChange(null);
          } else {
            onSelectedSpanChange(clickedSpanId);
          }
        } else {
          // Deselect if clicking outside any span
          onSelectedSpanChange(null);
        }
      }
    };

    chart.subscribeClick(handleClick);

    return () => {
      chart.unsubscribeClick(handleClick);
    };
  }, [chart, series, activeTool, interactionState, candles, selectedSpanId, onSelectedSpanChange]);

  // Handle mouse move for preview
  useEffect(() => {
    if (!chart || !series || activeTool !== 'span' || interactionState !== 'first-click-done' || !startCandle) {
      return;
    }

    const handleMouseMove = (param: MouseEventParams) => {
      if (!param.point) return;

      const time = chart.timeScale().coordinateToTime(param.point.x);
      if (!time) return;

      const timestamp = typeof time === 'string' ? Date.parse(time) / 1000 : (time as number);
      const nearestCandle = findNearestCandle(timestamp);

      if (!nearestCandle) return;

      // Update cursor candle for hotkey usage
      setCursorCandle(nearestCandle);

      // Calculate price range for preview
      const { max_high, min_low } = calculatePriceRange(startCandle, nearestCandle);

      // Swap if end < start
      const [start_time, end_time] =
        startCandle.time <= nearestCandle.time
          ? [startCandle.time, nearestCandle.time]
          : [nearestCandle.time, startCandle.time];

      const previewData: SpanData = {
        id: -1, // Preview ID
        start_time,
        end_time,
        label: 'PREVIEW',
        color: '#888888',
        max_high,
        min_low,
      };

      // Remove old preview primitive
      if (previewPrimitiveRef.current) {
        series.detachPrimitive(previewPrimitiveRef.current);
      }

      // Create new preview primitive
      const newPreview = new SpanRectanglePrimitive({
        data: previewData,
        isSelected: false,
      });

      series.attachPrimitive(newPreview);
      previewPrimitiveRef.current = newPreview;
    };

    chart.subscribeCrosshairMove(handleMouseMove);

    return () => {
      chart.unsubscribeCrosshairMove(handleMouseMove);
    };
  }, [chart, series, activeTool, interactionState, startCandle, candles]);

  // Handle Escape key to cancel span selection
  useEffect(() => {
    const handleKeyDown = (e: KeyboardEvent) => {
      if (e.key === 'Escape' && activeTool === 'span') {
        if (interactionState === 'first-click-done') {
          // Cancel span selection
          setInteractionState('idle');
          setStartCandle(null);
          setEndCandle(null);

          // Clean up preview
          if (previewPrimitiveRef.current && series) {
            series.detachPrimitive(previewPrimitiveRef.current);
            previewPrimitiveRef.current = null;
          }
        }
      }
    };

    window.addEventListener('keydown', handleKeyDown);
    return () => window.removeEventListener('keydown', handleKeyDown);
  }, [activeTool, interactionState, series]);

  // Handle span deletion
  const handleDeleteSpan = useCallback(async (spanId: number) => {
    try {
      const response = await fetch(`/api/span-annotations/${spanId}`, {
        method: 'DELETE',
      });

      if (response.ok) {
        onSpanAnnotationsChange();
        if (selectedSpanIdRef.current === spanId) {
          onSelectedSpanChange(null);
        }
      } else {
        console.error('Failed to delete span annotation');
      }
    } catch (error) {
      console.error('Error deleting span annotation:', error);
    }
  }, [onSpanAnnotationsChange, onSelectedSpanChange]);

  // Handle popover save
  const handlePopoverSave = async (data: {
    label: string;
    confidence: number | null;
    outcome: string | null;
    notes: string | null;
  }) => {
    // If editing existing span
    if (editingSpan) {
      // Find label type color
      const labelType = spanLabelTypes.find((t) => t.name === data.label);
      const color = labelType?.color || editingSpan.color;

      try {
        const response = await fetch(`/api/span-annotations/${editingSpan.id}`, {
          method: 'PATCH',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            label: data.label,
            confidence: data.confidence,
            outcome: data.outcome,
            notes: data.notes,
            color,
          }),
        });

        if (response.ok) {
          onSpanAnnotationsChange();
          setPopoverOpen(false);
          setEditingSpan(null);
        } else {
          console.error('Failed to update span annotation');
        }
      } catch (error) {
        console.error('Error updating span annotation:', error);
      }
      return;
    }

    // Creating new span
    if (!startCandle || !endCandle || !activeChartId) return;

    // Swap if end < start
    const [start_time, end_time] =
      startCandle.time <= endCandle.time
        ? [startCandle.time, endCandle.time]
        : [endCandle.time, startCandle.time];

    // Find label type color
    const labelType = spanLabelTypes.find((t) => t.name === data.label);
    const color = labelType?.color || '#2196F3';

    try {
      const response = await fetch('/api/span-annotations', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          chart_id: activeChartId,
          start_time,
          end_time,
          label: data.label,
          confidence: data.confidence,
          outcome: data.outcome,
          notes: data.notes,
          color,
        }),
      });

      if (response.ok) {
        onSpanAnnotationsChange();
        setPopoverOpen(false);
        setInteractionState('idle');
        setStartCandle(null);
        setEndCandle(null);
      } else {
        console.error('Failed to create span annotation');
      }
    } catch (error) {
      console.error('Error creating span annotation:', error);
    }
  };

  // Handle popover cancel
  const handlePopoverCancel = () => {
    setPopoverOpen(false);
    setInteractionState('idle');
    setStartCandle(null);
    setEndCandle(null);
    setEditingSpan(null);
  };

  // Handle keyboard shortcuts for deletion, editing, and hotkey label assignment
  useEffect(() => {
    const handleKeyDown = async (e: KeyboardEvent) => {
      // Delete/Backspace: delete selected span
      const currentSelectedSpanId = selectedSpanIdRef.current;
      if ((e.key === 'Delete' || e.key === 'Backspace') && currentSelectedSpanId !== null) {
        e.preventDefault();
        handleDeleteSpan(currentSelectedSpanId);
        return;
      }

      // Enter: open edit popover for selected span
      if (e.key === 'Enter' && currentSelectedSpanId !== null) {
        const span = spanAnnotations.find((s) => s.id === currentSelectedSpanId);
        if (span) {
          setEditingSpan(span);
          setPopoverOpen(true);
        }
        return;
      }

      // Hotkey label assignment: only when span tool is active and a span range is selected (after first click)
      if (activeTool === 'span' && interactionState === 'first-click-done' && startCandle) {
        // Check if key matches any label hotkey
        const matchingLabel = spanLabelTypes.find((lt) => lt.hotkey === e.key && lt.is_active);
        
        if (matchingLabel && activeChartId) {
          e.preventDefault();
          
          // Use cursor candle if available, otherwise use start candle
          const endCandleForHotkey = cursorCandle || startCandle;
          
          // Swap if end < start
          const [start_time, end_time] =
            startCandle.time <= endCandleForHotkey.time
              ? [startCandle.time, endCandleForHotkey.time]
              : [endCandleForHotkey.time, startCandle.time];

          // Create span with hotkey label (default confidence/outcome/notes)
          try {
            const response = await fetch('/api/span-annotations', {
              method: 'POST',
              headers: { 'Content-Type': 'application/json' },
              body: JSON.stringify({
                chart_id: activeChartId,
                start_time,
                end_time,
                label: matchingLabel.name,
                confidence: 3, // Default confidence
                outcome: null,
                notes: null,
                color: matchingLabel.color,
              }),
            });

            if (response.ok) {
              onSpanAnnotationsChange();
              
              // Clean up preview and reset state
              if (previewPrimitiveRef.current && series) {
                series.detachPrimitive(previewPrimitiveRef.current);
                previewPrimitiveRef.current = null;
              }
              setInteractionState('idle');
              setStartCandle(null);
              setEndCandle(null);
            }
          } catch (error) {
            console.error('Error creating span with hotkey:', error);
          }
        }
      }
    };

    window.addEventListener('keydown', handleKeyDown);
    return () => window.removeEventListener('keydown', handleKeyDown);
  }, [handleDeleteSpan, spanAnnotations, activeTool, interactionState, startCandle, cursorCandle, spanLabelTypes, activeChartId, series, onSpanAnnotationsChange]);

  // Handle double-click to edit span
  useEffect(() => {
    if (!chart) return;

    const handleDblClick = (param: MouseEventParams) => {
      if (!param.point) return;
      const point = param.point;

      let clickedSpanId: number | null = null;
      primitivesRef.current.forEach((primitive, spanId) => {
        if (primitive.hitTest(point.x, point.y)) {
          clickedSpanId = spanId;
        }
      });

      if (clickedSpanId !== null) {
        const span = spanAnnotations.find((s) => s.id === clickedSpanId);
        if (span) {
          setEditingSpan(span);
          setPopoverOpen(true);
        }
      }
    };

    chart.subscribeDblClick(handleDblClick);

    return () => {
      chart.unsubscribeDblClick(handleDblClick);
    };
  }, [chart, spanAnnotations]);

  return (
    <SpanPopover
      open={popoverOpen}
      onOpenChange={setPopoverOpen}
      spanLabelTypes={spanLabelTypes}
      initialData={
        editingSpan
          ? {
              start_time: editingSpan.start_time,
              end_time: editingSpan.end_time,
              label: editingSpan.label,
              confidence: editingSpan.confidence,
              outcome: editingSpan.outcome,
              notes: editingSpan.notes,
            }
          : startCandle && endCandle
          ? {
              start_time: Math.min(startCandle.time, endCandle.time),
              end_time: Math.max(startCandle.time, endCandle.time),
            }
          : undefined
      }
      onSave={handlePopoverSave}
      onCancel={handlePopoverCancel}
    />
  );
}
