import { useEffect, useMemo, useRef, useState } from 'react';

import { confirm } from '@renderer/components/common/ConfirmDialog';
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
import { useTaskLocalState } from '@renderer/hooks/useTaskLocalState';
import { cn } from '@renderer/lib/utils';
import { useStore } from '@renderer/store';
import { normalizePath } from '@renderer/utils/pathNormalize';
import { projectColor } from '@renderer/utils/projectColor';
import {
  getNonEmptyTaskCategories,
  groupTasksByDate,
  groupTasksByProject,
  sortTasksByFreshness,
} from '@renderer/utils/taskGrouping';
import { Archive, ListTodo, Pin, Search, X } from 'lucide-react';
import { useShallow } from 'zustand/react/shallow';

import { Combobox, type ComboboxOption } from '../ui/combobox';

import { SidebarTaskItem } from './SidebarTaskItem';
import { TaskContextMenu } from './TaskContextMenu';
import { TaskFiltersPopover } from './TaskFiltersPopover';
import {
  defaultTaskFiltersState,
  getTaskUnreadCount,
  taskMatchesStatus,
  useReadStateSnapshot,
} from './taskFiltersState';

import type { TaskFiltersState } from './taskFiltersState';
import type { GlobalTask } from '@shared/types';

const TASK_GROUPING_STORAGE_KEY = 'sidebarTasksGrouping';

export type TaskGroupingMode = 'none' | 'project' | 'time';

function loadGroupingMode(): TaskGroupingMode {
  try {
    const v = localStorage.getItem(TASK_GROUPING_STORAGE_KEY);
    if (v === 'none' || v === 'project' || v === 'time') return v;
  } catch {
    /* ignore */
  }
  return 'none';
}

function saveGroupingMode(mode: TaskGroupingMode): void {
  try {
    localStorage.setItem(TASK_GROUPING_STORAGE_KEY, mode);
  } catch {
    /* ignore */
  }
}

export interface GlobalTaskListProps {
  /** When true, do not render the header row (Tasks + Filters); parent renders tabs and filters. */
  hideHeader?: boolean;
  /** External filters state when used with sidebar tabs. */
  filters?: TaskFiltersState;
  onFiltersChange?: (f: TaskFiltersState) => void;
  filtersPopoverOpen?: boolean;
  onFiltersPopoverOpenChange?: (open: boolean) => void;
}

const dateCategoryLabels: Record<string, string> = {
  'Previous 7 Days': 'Last 7 Days',
  Older: 'Earlier',
};

function applySearch(tasks: GlobalTask[], query: string): GlobalTask[] {
  if (!query.trim()) return tasks;
  const q = query.toLowerCase();
  return tasks.filter(
    (t) =>
      t.subject.toLowerCase().includes(q) ||
      t.owner?.toLowerCase().includes(q) ||
      t.teamDisplayName.toLowerCase().includes(q)
  );
}

function applyProjectFilter(tasks: GlobalTask[], projectPath: string | null): GlobalTask[] {
  if (!projectPath) return tasks;
  const normalized = normalizePath(projectPath);
  return tasks.filter((t) => t.projectPath && normalizePath(t.projectPath) === normalized);
}

export const GlobalTaskList = ({
  hideHeader = false,
  filters: externalFilters,
  onFiltersChange: externalOnFiltersChange,
  filtersPopoverOpen: externalFiltersPopoverOpen,
  onFiltersPopoverOpenChange: externalOnFiltersPopoverOpenChange,
}: GlobalTaskListProps = {}): React.JSX.Element => {
  const {
    globalTasks,
    globalTasksLoading,
    globalTasksInitialized,
    fetchAllTasks,
    softDeleteTask,
    projects,
    viewMode,
    repositoryGroups,
    teams,
  } = useStore(
    useShallow((s) => ({
      globalTasks: s.globalTasks,
      globalTasksLoading: s.globalTasksLoading,
      globalTasksInitialized: s.globalTasksInitialized,
      fetchAllTasks: s.fetchAllTasks,
      softDeleteTask: s.softDeleteTask,
      projects: s.projects,
      viewMode: s.viewMode,
      repositoryGroups: s.repositoryGroups,
      teams: s.teams,
    }))
  );

  const [internalFilters, setInternalFilters] = useState(defaultTaskFiltersState);
  const [internalFiltersPopoverOpen, setInternalFiltersPopoverOpen] = useState(false);
  const filters = externalFilters ?? internalFilters;
  const setFilters = externalOnFiltersChange ?? setInternalFilters;
  const filtersPopoverOpen = externalFiltersPopoverOpen ?? internalFiltersPopoverOpen;
  const setFiltersPopoverOpen = externalOnFiltersPopoverOpenChange ?? setInternalFiltersPopoverOpen;
  const [searchQuery, setSearchQuery] = useState('');
  const [groupingMode, setGroupingModeState] = useState<TaskGroupingMode>(loadGroupingMode);
  const [showArchived, setShowArchived] = useState(false);
  const [renamingTaskKey, setRenamingTaskKey] = useState<string | null>(null);
  const searchInputRef = useRef<HTMLInputElement>(null);
  const hasFetchedRef = useRef(false);
  const readState = useReadStateSnapshot();
  const taskLocalState = useTaskLocalState();

  // Local project filter (independent from sessions tab)
  const [localProjectFilter, setLocalProjectFilter] = useState<string | null>(null);

  const setGroupingMode = (mode: TaskGroupingMode): void => {
    setGroupingModeState(mode);
    saveGroupingMode(mode);
  };

  const handleRenameComplete = (teamName: string, taskId: string, newSubject: string): void => {
    taskLocalState.renameTask(teamName, taskId, newSubject);
    setRenamingTaskKey(null);
  };

  const handleRenameCancel = (): void => {
    setRenamingTaskKey(null);
  };

  const handleDeleteTask = async (teamName: string, taskId: string): Promise<void> => {
    const confirmed = await confirm({
      title: 'Delete task',
      message: `Move task #${taskId} to trash?`,
      confirmLabel: 'Delete',
      cancelLabel: 'Cancel',
      variant: 'danger',
    });
    if (confirmed) {
      try {
        await softDeleteTask(teamName, taskId);
        await fetchAllTasks();
      } catch (err) {
        void confirm({
          title: 'Failed to delete task',
          message: err instanceof Error ? err.message : 'An unexpected error occurred',
          confirmLabel: 'OK',
          variant: 'danger',
        });
      }
    }
  };

  // Fetch tasks on mount — loading guard in the store action prevents
  // duplicate IPC calls when the centralized init chain is already fetching.
  useEffect(() => {
    if (!hasFetchedRef.current && !globalTasksLoading) {
      hasFetchedRef.current = true;
      void fetchAllTasks();
    }
  }, [fetchAllTasks, globalTasksLoading]);

  // Build project combobox options from available projects/repos
  const projectFilterOptions = useMemo((): ComboboxOption[] => {
    const items =
      viewMode === 'grouped'
        ? repositoryGroups
            .filter((r) => r.totalSessions > 0)
            .map((r) => ({
              value: r.worktrees[0]?.path ?? r.id,
              label: r.name,
              path: r.worktrees[0]?.path,
            }))
        : projects
            .filter((p) => (p.totalSessions ?? p.sessions.length) > 0)
            .map((p) => ({
              value: p.path,
              label: p.name,
              path: p.path,
            }));

    return items.map((item) => ({
      value: item.value,
      label: item.label,
      description: item.path,
    }));
  }, [viewMode, repositoryGroups, projects]);

  // Resolve local filter to a project path
  const selectedProjectPath = localProjectFilter;

  const filtered = useMemo(() => {
    let result = globalTasks;
    result = applyProjectFilter(result, selectedProjectPath);
    result = result.filter((t) => taskMatchesStatus(t, filters.statusIds));
    if (filters.teamName) {
      result = result.filter((t) => t.teamName === filters.teamName);
    }
    if (filters.unreadOnly) {
      result = result.filter(
        (t) => getTaskUnreadCount(readState, t.teamName, t.id, t.comments) > 0
      );
    }
    result = applySearch(result, searchQuery);
    // Archive filtering
    if (showArchived) {
      result = result.filter((t) => taskLocalState.isArchived(t.teamName, t.id));
    } else {
      result = result.filter((t) => !taskLocalState.isArchived(t.teamName, t.id));
    }
    return result;
  }, [
    globalTasks,
    selectedProjectPath,
    filters.statusIds,
    filters.teamName,
    filters.unreadOnly,
    searchQuery,
    readState,
    showArchived,
    taskLocalState,
  ]);

  // Check if any archived tasks exist (before archive filtering) to conditionally show the toggle
  const hasArchivedTasks = useMemo(
    () => globalTasks.some((t) => taskLocalState.isArchived(t.teamName, t.id)),
    [globalTasks, taskLocalState]
  );

  // Reset showArchived when archive becomes empty
  useEffect(() => {
    if (showArchived && !hasArchivedTasks) {
      // eslint-disable-next-line react-hooks/set-state-in-effect -- intentional sync on prop change
      setShowArchived(false);
    }
  }, [showArchived, hasArchivedTasks]);

  // Split into pinned and normal (non-pinned) tasks
  const pinnedTasks = useMemo(
    () => filtered.filter((t) => taskLocalState.isPinned(t.teamName, t.id)),
    [filtered, taskLocalState]
  );
  const normalTasks = useMemo(
    () => filtered.filter((t) => !taskLocalState.isPinned(t.teamName, t.id)),
    [filtered, taskLocalState]
  );

  const sortedFlat = useMemo(() => sortTasksByFreshness(normalTasks), [normalTasks]);
  const grouped = useMemo(() => groupTasksByDate(normalTasks), [normalTasks]);
  const categories = useMemo(() => getNonEmptyTaskCategories(grouped), [grouped]);
  const projectGroups = useMemo(() => groupTasksByProject(normalTasks), [normalTasks]);

  const hasContent =
    pinnedTasks.length > 0 ||
    (groupingMode === 'none'
      ? sortedFlat.length > 0
      : groupingMode === 'time'
        ? categories.length > 0
        : projectGroups.some((g) => g.tasks.length > 0));

  return (
    <div className="flex size-full min-w-0 flex-col">
      {!hideHeader && (
        <div
          className="flex shrink-0 items-center gap-2 border-b px-3 py-1.5"
          style={{ borderColor: 'var(--color-border)' }}
        >
          <span className="text-[12px] font-semibold text-text-secondary">Tasks</span>
        </div>
      )}

      {/* Search bar */}
      <div
        className="mb-[5px] flex shrink-0 items-center gap-1.5 border-b px-2 py-1"
        style={{ borderColor: 'var(--color-border)' }}
      >
        <Search className="size-3 shrink-0 text-text-muted" />
        <input
          ref={searchInputRef}
          type="text"
          placeholder="Search tasks..."
          value={searchQuery}
          onChange={(e) => setSearchQuery(e.target.value)}
          className="min-w-0 flex-1 bg-transparent text-[12px] text-text placeholder:text-text-muted focus:outline-none"
        />
        {searchQuery && (
          <button
            type="button"
            className="shrink-0 text-text-muted hover:text-text-secondary"
            onClick={() => {
              setSearchQuery('');
              searchInputRef.current?.focus();
            }}
          >
            <X className="size-3" />
          </button>
        )}
        <TaskFiltersPopover
          open={filtersPopoverOpen}
          onOpenChange={setFiltersPopoverOpen}
          teams={teams.map((t) => ({ teamName: t.teamName, displayName: t.displayName }))}
          filters={filters}
          onFiltersChange={setFilters}
          onApply={() => {}}
        />
      </div>

      {/* Project filter */}
      <div className="shrink-0 px-2 py-1">
        <Combobox
          options={projectFilterOptions}
          value={localProjectFilter ?? ''}
          onValueChange={(v) => setLocalProjectFilter(v)}
          placeholder="All Projects"
          searchPlaceholder="Search projects..."
          emptyMessage="No projects"
          className="text-[11px]"
          resetLabel="All Projects"
          onReset={() => setLocalProjectFilter(null)}
        />
      </div>

      {/* Pinned tasks section */}
      {pinnedTasks.length > 0 && !showArchived && (
        <div className="shrink-0 border-b" style={{ borderColor: 'var(--color-border)' }}>
          <div className="flex items-center gap-1 px-2 py-1">
            <Pin className="size-3 text-text-muted" />
            <span className="text-[11px] text-text-muted">Pinned</span>
          </div>
          {sortTasksByFreshness(pinnedTasks).map((task) => (
            <TaskContextMenu
              key={`pinned-${task.teamName}-${task.id}`}
              task={task}
              isPinned={true}
              isArchived={false}
              onTogglePin={() => taskLocalState.togglePin(task.teamName, task.id)}
              onToggleArchive={() => taskLocalState.toggleArchive(task.teamName, task.id)}
              onRename={() => setRenamingTaskKey(`${task.teamName}:${task.id}`)}
              onDelete={() => handleDeleteTask(task.teamName, task.id)}
            >
              <SidebarTaskItem
                task={task}
                showTeamName
                renamingKey={renamingTaskKey}
                onRenameComplete={handleRenameComplete}
                onRenameCancel={handleRenameCancel}
                getDisplaySubject={(t) => taskLocalState.getRenamedSubject(t.teamName, t.id)}
              />
            </TaskContextMenu>
          ))}
        </div>
      )}

      {/* Grouping mode — compact segmented toggle */}
      <div className="flex shrink-0 items-center gap-1.5 px-2 py-1">
        <span className="shrink-0 text-[11px] text-text-muted">Group by:</span>
        <div
          className="bg-surface-raised/60 inline-flex rounded-md p-0.5 text-[11px]"
          role="group"
          aria-label="Group by"
        >
          {(['none', 'project', 'time'] as const).map((mode) => {
            const label = mode === 'none' ? 'None' : mode === 'project' ? 'Project' : 'Time';
            return (
              <button
                key={mode}
                type="button"
                onClick={() => setGroupingMode(mode)}
                className={cn(
                  'rounded px-2 py-0.5 transition-colors',
                  groupingMode === mode
                    ? 'bg-surface-raised text-text shadow-sm'
                    : 'text-text-muted hover:text-text-secondary'
                )}
              >
                {label}
              </button>
            );
          })}
        </div>
        {/* Archive toggle — only visible when archived tasks exist */}
        {hasArchivedTasks && (
          <div className="ml-auto">
            <Tooltip>
              <TooltipTrigger asChild>
                <button
                  type="button"
                  onClick={() => setShowArchived(!showArchived)}
                  className={cn(
                    'rounded p-0.5 transition-colors',
                    showArchived
                      ? 'bg-surface-raised text-text-secondary'
                      : 'text-text-muted hover:text-text-secondary'
                  )}
                >
                  <Archive className="size-3.5" />
                </button>
              </TooltipTrigger>
              <TooltipContent side="top">
                {showArchived ? 'Hide archived' : 'Show archived'}
              </TooltipContent>
            </Tooltip>
          </div>
        )}
      </div>

      {/* Content */}
      <div className="flex-1 overflow-y-auto">
        {globalTasksLoading && !globalTasksInitialized && (
          <div className="space-y-2 p-3">
            {[1, 2, 3].map((i) => (
              <div key={i} className="h-[48px] animate-pulse rounded bg-surface-raised" />
            ))}
          </div>
        )}

        {globalTasksInitialized && !hasContent && (
          <div className="flex flex-col items-center gap-2 px-4 py-8 text-text-muted">
            <ListTodo className="size-8 opacity-40" />
            <span className="text-[12px]">
              {searchQuery || selectedProjectPath ? 'No matching tasks' : 'No tasks found'}
            </span>
          </div>
        )}

        {groupingMode === 'none' &&
          sortedFlat.map((task) => (
            <TaskContextMenu
              key={`${task.teamName}-${task.id}`}
              task={task}
              isPinned={taskLocalState.isPinned(task.teamName, task.id)}
              isArchived={taskLocalState.isArchived(task.teamName, task.id)}
              onTogglePin={() => taskLocalState.togglePin(task.teamName, task.id)}
              onToggleArchive={() => taskLocalState.toggleArchive(task.teamName, task.id)}
              onRename={() => setRenamingTaskKey(`${task.teamName}:${task.id}`)}
              onDelete={() => handleDeleteTask(task.teamName, task.id)}
            >
              <SidebarTaskItem
                task={task}
                showTeamName
                renamingKey={renamingTaskKey}
                onRenameComplete={handleRenameComplete}
                onRenameCancel={handleRenameCancel}
                getDisplaySubject={(t) => taskLocalState.getRenamedSubject(t.teamName, t.id)}
              />
            </TaskContextMenu>
          ))}

        {groupingMode === 'project' &&
          projectGroups.map((group) => {
            if (group.tasks.length === 0) return null;
            let lastTeam: string | null = null;
            return (
              <div key={group.projectKey}>
                <div
                  className="sticky top-0 z-10 flex items-center gap-1.5 px-3 py-1.5 text-[11px] font-semibold"
                  style={{ backgroundColor: 'var(--color-surface-sidebar)' }}
                >
                  <span
                    className="inline-block size-1.5 shrink-0 rounded-full"
                    style={{ backgroundColor: projectColor(group.projectLabel).border }}
                  />
                  <span style={{ color: projectColor(group.projectLabel).text }}>
                    {group.projectLabel}
                  </span>
                </div>
                {group.tasks.map((task) => {
                  const showTeamHeader = task.teamName !== lastTeam;
                  lastTeam = task.teamName;
                  return (
                    <div key={`${task.teamName}-${task.id}`}>
                      {showTeamHeader && (
                        <div className="px-3 pb-0.5 pt-1.5 text-[10px] font-medium text-text-muted">
                          Team: {task.teamDisplayName}
                        </div>
                      )}
                      <TaskContextMenu
                        task={task}
                        isPinned={taskLocalState.isPinned(task.teamName, task.id)}
                        isArchived={taskLocalState.isArchived(task.teamName, task.id)}
                        onTogglePin={() => taskLocalState.togglePin(task.teamName, task.id)}
                        onToggleArchive={() => taskLocalState.toggleArchive(task.teamName, task.id)}
                        onRename={() => setRenamingTaskKey(`${task.teamName}:${task.id}`)}
                        onDelete={() => handleDeleteTask(task.teamName, task.id)}
                      >
                        <SidebarTaskItem
                          task={task}
                          hideTeamName
                          renamingKey={renamingTaskKey}
                          onRenameComplete={handleRenameComplete}
                          onRenameCancel={handleRenameCancel}
                          getDisplaySubject={(t) =>
                            taskLocalState.getRenamedSubject(t.teamName, t.id)
                          }
                        />
                      </TaskContextMenu>
                    </div>
                  );
                })}
              </div>
            );
          })}

        {groupingMode === 'time' &&
          categories.map((category) => {
            const tasks = grouped[category];
            let lastTeam: string | null = null;

            return (
              <div key={category}>
                <div
                  className="sticky top-0 z-10 px-3 py-1.5 text-[11px] font-semibold text-text-secondary"
                  style={{ backgroundColor: 'var(--color-surface-sidebar)' }}
                >
                  {dateCategoryLabels[category] ?? category}
                </div>

                {tasks.map((task) => {
                  const showTeamHeader = task.teamName !== lastTeam;
                  lastTeam = task.teamName;

                  return (
                    <div key={`${task.teamName}-${task.id}`}>
                      {showTeamHeader && (
                        <div className="px-3 pb-0.5 pt-1.5 text-[10px] font-medium text-text-muted">
                          Team: {task.teamDisplayName}
                        </div>
                      )}
                      <TaskContextMenu
                        task={task}
                        isPinned={taskLocalState.isPinned(task.teamName, task.id)}
                        isArchived={taskLocalState.isArchived(task.teamName, task.id)}
                        onTogglePin={() => taskLocalState.togglePin(task.teamName, task.id)}
                        onToggleArchive={() => taskLocalState.toggleArchive(task.teamName, task.id)}
                        onRename={() => setRenamingTaskKey(`${task.teamName}:${task.id}`)}
                        onDelete={() => handleDeleteTask(task.teamName, task.id)}
                      >
                        <SidebarTaskItem
                          task={task}
                          renamingKey={renamingTaskKey}
                          onRenameComplete={handleRenameComplete}
                          onRenameCancel={handleRenameCancel}
                          getDisplaySubject={(t) =>
                            taskLocalState.getRenamedSubject(t.teamName, t.id)
                          }
                        />
                      </TaskContextMenu>
                    </div>
                  );
                })}
              </div>
            );
          })}
      </div>
    </div>
  );
};
