/**
 * ToDo section for the Radar sidebar with lifecycle actions.
 *
 * Displays active ToDo items (status ``pending`` or ``overdue``) fetched via
 * ``radarService.fetchActiveTodos``.  Items are sorted by priority (high first)
 * then creation date (newest first).  A display limit of 10 items is enforced
 * by default with "See more" / "Show less" expansion controls.
 *
 * Each item row is fully draggable (payload type ``radar-todo``) and shows
 * hover action buttons: ✅ mark handled, ❌ dismiss (cancel).  Actions use
 * optimistic removal — the item disappears immediately, confirmed on next poll.
 *
 * Key exports:
 * - ``TodoSection``          — The section component
 * - ``PRIORITY_WEIGHT``      — Priority-to-number mapping for sorting
 * - ``PRIORITY_COLORS``      — Priority-to-CSS-color mapping for dots
 * - ``filterActiveTodos``    — Filters to pending/overdue only
 * - ``sortByPriorityThenDate`` — Sorts by priority desc, then createdAt desc
 */

import { useState, useEffect, useMemo, useRef, useCallback } from 'react';
import type { RadarTodo } from '../../../../types';
import { radarService } from '../../../../services/radar';
import { todosService } from '../../../../services/todos';
import type { DropPayload } from './types';

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

/** Default number of items shown before "See more" expansion. */
const DISPLAY_LIMIT = 10;

/** Polling interval for background refresh (ms). */
const POLL_INTERVAL_MS = 30_000;

/** Numeric weight per priority level (higher = more important). */
export const PRIORITY_WEIGHT: Record<string, number> = {
  high: 3,
  medium: 2,
  low: 1,
  none: 0,
};

/** CSS color value for each priority level's dot indicator. */
export const PRIORITY_COLORS: Record<string, string> = {
  high: 'var(--color-error, #ef4444)',
  medium: 'var(--color-warning, #f59e0b)',
  low: 'var(--color-info, #3b82f6)',
  none: 'var(--color-text-muted, #9ca3af)',
};

// ---------------------------------------------------------------------------
// Pure helpers (exported for testing)
// ---------------------------------------------------------------------------

/** Return only active (not handled/cancelled/deleted) ToDo items. */
export function filterActiveTodos(todos: RadarTodo[]): RadarTodo[] {
  const inactive = new Set(['handled', 'cancelled', 'deleted']);
  return todos.filter((t) => !inactive.has(t.status));
}

/**
 * Sort by priority descending (high=3 first), then createdAt descending
 * (newest first).  Uses id as a deterministic tiebreaker.
 */
export function sortByPriorityThenDate(todos: RadarTodo[]): RadarTodo[] {
  return [...todos].sort((a, b) => {
    const aPri = PRIORITY_WEIGHT[a.priority] ?? 0;
    const bPri = PRIORITY_WEIGHT[b.priority] ?? 0;
    if (aPri !== bPri) return bPri - aPri;

    // Newest first — reverse lexicographic on ISO date strings
    if (a.createdAt !== b.createdAt) {
      return a.createdAt > b.createdAt ? -1 : 1;
    }

    // Deterministic tiebreaker
    return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
  });
}

// ---------------------------------------------------------------------------
// Props
// ---------------------------------------------------------------------------

interface TodoSectionProps {
  workspaceId: string | null;
  /** Report item count to parent for badge display. */
  onCountChange?: (count: number) => void;
  /** Click title → populate ChatInput with work packet context. */
  onItemClick?: (message: string, context?: string) => void;
}

// ---------------------------------------------------------------------------
// Component
// ---------------------------------------------------------------------------

export function TodoSection({ workspaceId, onCountChange, onItemClick }: TodoSectionProps) {
  const [todos, setTodos] = useState<RadarTodo[]>([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [expanded, setExpanded] = useState(false);

  // Track whether initial load has completed (show spinner only for first load)
  const hasLoadedRef = useRef(false);
  // Track in-flight action to show optimistic UI
  const [actingOn, setActingOn] = useState<string | null>(null);

  // Quick-action handlers
  const handleMarkHandled = useCallback(async (todoId: string) => {
    setActingOn(todoId);
    try {
      await todosService.markHandled(todoId);
      // Optimistically remove from list
      setTodos((prev) => prev.filter((t) => t.id !== todoId));
    } catch (err) {
      console.error('[TodoSection] Failed to mark handled:', err);
    } finally {
      setActingOn(null);
    }
  }, []);

  const handleMarkCancelled = useCallback(async (todoId: string) => {
    setActingOn(todoId);
    try {
      await todosService.markCancelled(todoId);
      setTodos((prev) => prev.filter((t) => t.id !== todoId));
    } catch (err) {
      console.error('[TodoSection] Failed to mark cancelled:', err);
    } finally {
      setActingOn(null);
    }
  }, []);

  // Stable fetch callback — silent=true skips the loading spinner (for polls)
  const fetchTodos = useCallback(
    async (silent: boolean) => {
      if (!workspaceId) {
        setTodos([]);
        return;
      }
      if (!silent) setLoading(true);
      setError(null);

      try {
        const data = await radarService.fetchActiveTodos(workspaceId);
        setTodos(data);
        hasLoadedRef.current = true;
      } catch (err) {
        // Only show error on initial load — silent polls swallow errors
        if (!silent) {
          setError(err instanceof Error ? err.message : 'Failed to load todos');
        }
      } finally {
        if (!silent) setLoading(false);
      }
    },
    [workspaceId],
  );

  // Initial fetch + reset on workspace change
  useEffect(() => {
    setExpanded(false);
    hasLoadedRef.current = false;
    fetchTodos(false);
  }, [fetchTodos]);

  // 30s polling — silent background refresh
  useEffect(() => {
    if (!workspaceId) return;
    const id = setInterval(() => fetchTodos(true), POLL_INTERVAL_MS);
    return () => clearInterval(id);
  }, [workspaceId, fetchTodos]);

  // Visibility change — refetch when user returns to the app/tab
  useEffect(() => {
    if (!workspaceId) return;
    const handleVisibility = () => {
      if (document.visibilityState === 'visible') {
        fetchTodos(true);
      }
    };
    document.addEventListener('visibilitychange', handleVisibility);
    // Also refetch on window focus (covers alt-tab without visibility change)
    window.addEventListener('focus', handleVisibility);
    return () => {
      document.removeEventListener('visibilitychange', handleVisibility);
      window.removeEventListener('focus', handleVisibility);
    };
  }, [workspaceId, fetchTodos]);

  // Derive active, sorted list
  const activeTodos = useMemo(
    () => sortByPriorityThenDate(filterActiveTodos(todos)),
    [todos],
  );

  // Report count to parent — use ref to avoid re-render loops
  const prevCountRef = useRef(-1);
  useEffect(() => {
    if (onCountChange && activeTodos.length !== prevCountRef.current) {
      prevCountRef.current = activeTodos.length;
      onCountChange(activeTodos.length);
    }
  }, [activeTodos.length, onCountChange]);

  const visibleTodos = expanded
    ? activeTodos
    : activeTodos.slice(0, DISPLAY_LIMIT);
  const remaining = activeTodos.length - DISPLAY_LIMIT;

  // --- Loading state ---
  if (loading) {
    return (
      <p className="text-xs text-[var(--color-text-muted)] py-2">
        Loading todos…
      </p>
    );
  }

  // --- Error state ---
  if (error) {
    return (
      <p className="text-xs text-[var(--color-error)] py-2">
        {error}
      </p>
    );
  }

  // --- Empty state ---
  if (activeTodos.length === 0) {
    return (
      <p className="text-xs text-[var(--color-text-muted)] py-2">
        No active todos
      </p>
    );
  }

  // --- Item list ---
  return (
    <div>
      <ul className="space-y-1">
        {visibleTodos.map((todo) => {
          const payload: DropPayload = {
            type: 'radar-todo',
            id: todo.id,
            title: todo.title,
            context: todo.linkedContext ?? todo.description ?? undefined,
          };
          const dotColor =
            PRIORITY_COLORS[todo.priority] ?? PRIORITY_COLORS.none;

          const isActing = actingOn === todo.id;

          return (
            <li
              key={todo.id}
              draggable="true"
              onDragStart={(e) => {
                e.dataTransfer.setData('application/json', JSON.stringify(payload));
                e.dataTransfer.effectAllowed = 'copy';
                e.currentTarget.style.opacity = '0.4';
              }}
              onDragEnd={(e) => {
                e.currentTarget.style.opacity = '1';
              }}
              onClick={() => {
                if (onItemClick) {
                  const ctx = payload.context || '';
                  onItemClick(`[ToDo:${todo.id}] ${todo.title}`, ctx);
                }
              }}
              className={`group flex items-center gap-2 px-1 py-1 rounded hover:bg-[var(--color-hover)] ${onItemClick ? 'cursor-pointer' : 'cursor-grab'} active:cursor-grabbing transition-colors ${isActing ? 'opacity-50' : ''}`}
            >
              {/* Priority dot */}
              <span
                className="shrink-0 w-2 h-2 rounded-full"
                style={{ backgroundColor: dotColor }}
                title={`Priority: ${todo.priority}`}
              />

              {/* Title */}
              <span className="text-[13px] leading-5 text-[var(--color-text)] truncate flex-1 select-none">
                {todo.title}
              </span>

              {/* Action buttons — visible on hover */}
              <span
                className="shrink-0 flex items-center gap-0.5 opacity-0 group-hover:opacity-100 transition-opacity"
                onDragStart={(e) => e.stopPropagation()}
                draggable={false}
              >
                <button
                  className="p-0.5 rounded hover:bg-[var(--color-success,#22c55e)]/20 text-[var(--color-success,#22c55e)] transition-colors"
                  title="Mark as done"
                  onClick={(e) => { e.stopPropagation(); handleMarkHandled(todo.id); }}
                  disabled={isActing}
                >
                  <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                    <polyline points="3.5 8 6.5 11 12.5 5" />
                  </svg>
                </button>
                <button
                  className="p-0.5 rounded hover:bg-[var(--color-error,#ef4444)]/20 text-[var(--color-text-muted)] hover:text-[var(--color-error,#ef4444)] transition-colors"
                  title="Dismiss"
                  onClick={(e) => { e.stopPropagation(); handleMarkCancelled(todo.id); }}
                  disabled={isActing}
                >
                  <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                    <line x1="4" y1="4" x2="12" y2="12" />
                    <line x1="12" y1="4" x2="4" y2="12" />
                  </svg>
                </button>
              </span>
            </li>
          );
        })}
      </ul>

      {/* See more / Show less controls */}
      {remaining > 0 && !expanded && (
        <button
          className="text-xs text-[var(--color-link)] hover:underline mt-1 px-1"
          onClick={() => setExpanded(true)}
        >
          See more ({remaining} more)
        </button>
      )}
      {expanded && activeTodos.length > DISPLAY_LIMIT && (
        <button
          className="text-xs text-[var(--color-link)] hover:underline mt-1 px-1"
          onClick={() => setExpanded(false)}
        >
          Show less
        </button>
      )}
    </div>
  );
}
