import { memo, useEffect, useMemo, useReducer, useState } from 'react';

import { useAppTranslation } from '@features/localization/renderer';
import { OngoingIndicator } from '@renderer/components/common/OngoingIndicator';
import { MemberBadge } from '@renderer/components/team/MemberBadge';
import { UnreadCommentsBadge } from '@renderer/components/team/UnreadCommentsBadge';
import { Button } from '@renderer/components/ui/button';
import { Popover, PopoverContent, PopoverTrigger } from '@renderer/components/ui/popover';
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
import { useTheme } from '@renderer/hooks/useTheme';
import { useUnreadCommentCount } from '@renderer/hooks/useUnreadCommentCount';
import { REVIEW_STATE_DISPLAY } from '@renderer/utils/memberHelpers';
import {
  buildTaskChangeRequestOptions,
  canDisplayTaskChangesForOptions,
} from '@renderer/utils/taskChangeRequest';
import { deriveTaskDisplayId, formatTaskDisplayLabel } from '@shared/utils/taskIdentity';
import {
  isTeamTaskFinishedForDependency,
  isTeamTaskNeedsFixActionable,
} from '@shared/utils/teamTaskState';
import {
  ArrowLeftFromLine,
  ArrowRightFromLine,
  CheckCircle2,
  Eye,
  FileCode,
  FilePenLine,
  HelpCircle,
  Play,
  RotateCcw,
  Trash2,
  XCircle,
} from 'lucide-react';

import type {
  KanbanColumnId,
  KanbanTaskState,
  TaskComment,
  TeamTask,
  TeamTaskWithKanban,
} from '@shared/types';

interface KanbanTaskCardProps {
  task: TeamTaskWithKanban;
  teamName: string;
  columnId: KanbanColumnId;
  kanbanTaskState?: KanbanTaskState;
  hasReviewers: boolean;
  compact?: boolean;
  taskMap: Map<string, TeamTask>;
  memberColorMap: Map<string, string>;
  hasLiveTaskLogs?: boolean;
  onRequestReview: (taskId: string) => void;
  onApprove: (taskId: string) => void;
  onRequestChanges: (taskId: string) => void;
  onMoveBackToDone: (taskId: string) => void;
  onStartTask: (taskId: string) => void;
  onCompleteTask: (taskId: string) => void;
  onCancelTask: (taskId: string) => void;
  onScrollToTask?: (taskId: string) => void;
  onTaskClick?: (task: TeamTask) => void;
  onViewChanges?: (taskId: string) => void;
  onDeleteTask?: (taskId: string) => void;
}

interface DependencyBadgeProps {
  taskId: string;
  taskMap: Map<string, TeamTask>;
  onScrollToTask?: (taskId: string) => void;
}

interface CommentPulseState {
  taskKey: string;
  commentCount: number;
  commentIds: Set<string>;
  pulseKey: number;
}

interface CommentPulseSyncAction {
  taskKey: string;
  comments: readonly TaskComment[];
}

const EMPTY_TASK_COMMENTS: readonly TaskComment[] = [];
const taskCardSignatureCache = new WeakMap<TeamTaskWithKanban, string>();

function getTaskCardSignature(task: TeamTaskWithKanban): string {
  const cached = taskCardSignatureCache.get(task);
  if (cached !== undefined) return cached;

  const signature = JSON.stringify(task);
  taskCardSignatureCache.set(task, signature);
  return signature;
}

function areKanbanTaskStatesEqual(
  prev: KanbanTaskState | undefined,
  next: KanbanTaskState | undefined
): boolean {
  if (prev === next) return true;
  if (!prev || !next) return !prev && !next;
  return (
    prev.column === next.column &&
    prev.reviewer === next.reviewer &&
    prev.errorDescription === next.errorDescription &&
    prev.movedAt === next.movedAt
  );
}

function getTaskDependencyIds(task: TeamTaskWithKanban): string[] {
  return [...(task.blockedBy ?? []), ...(task.blocks ?? [])].filter((id) => id.length > 0);
}

function getDependencyTaskSignature(task: TeamTask | undefined): string {
  if (!task) return '';
  const kanbanTask = task as Partial<TeamTaskWithKanban>;
  return [
    task.id,
    task.displayId ?? '',
    task.subject,
    task.status,
    task.reviewState ?? '',
    kanbanTask.kanbanColumn ?? '',
  ].join('\u001f');
}

function areTaskMapDependenciesEqual(
  prevTask: TeamTaskWithKanban,
  nextTask: TeamTaskWithKanban,
  prevTaskMap: Map<string, TeamTask>,
  nextTaskMap: Map<string, TeamTask>
): boolean {
  const dependencyIds = new Set([
    ...getTaskDependencyIds(prevTask),
    ...getTaskDependencyIds(nextTask),
  ]);
  for (const taskId of dependencyIds) {
    if (
      getDependencyTaskSignature(prevTaskMap.get(taskId)) !==
      getDependencyTaskSignature(nextTaskMap.get(taskId))
    ) {
      return false;
    }
  }
  return true;
}

function createCommentPulseState(
  taskKey: string,
  comments: readonly TaskComment[],
  pulseKey = 0
): CommentPulseState {
  return {
    taskKey,
    commentCount: comments.length,
    commentIds: new Set(comments.map((comment) => comment.id)),
    pulseKey,
  };
}

function hasSameCommentIds(state: CommentPulseState, comments: readonly TaskComment[]): boolean {
  return (
    comments.length === state.commentCount &&
    comments.every((comment) => state.commentIds.has(comment.id))
  );
}

function syncCommentPulseState(
  state: CommentPulseState,
  action: CommentPulseSyncAction
): CommentPulseState {
  if (state.taskKey !== action.taskKey) {
    return createCommentPulseState(action.taskKey, action.comments);
  }

  const hasNewIncomingComment =
    action.comments.length > state.commentCount &&
    action.comments.some(
      (comment) => !state.commentIds.has(comment.id) && comment.author !== 'user'
    );

  if (!hasNewIncomingComment && hasSameCommentIds(state, action.comments)) {
    return state;
  }

  return createCommentPulseState(
    action.taskKey,
    action.comments,
    hasNewIncomingComment ? state.pulseKey + 1 : state.pulseKey
  );
}

const DependencyBadge = ({
  taskId,
  taskMap,
  onScrollToTask,
}: DependencyBadgeProps): React.JSX.Element => {
  const depTask = taskMap.get(taskId);
  const isCompleted = depTask ? isTeamTaskFinishedForDependency(depTask) : false;
  const label = depTask
    ? `${formatTaskDisplayLabel(depTask)}: ${depTask.subject}`
    : `#${deriveTaskDisplayId(taskId)}`;

  return (
    <Tooltip>
      <TooltipTrigger asChild>
        <button
          type="button"
          className={`inline-flex items-center rounded px-1.5 py-0.5 text-[10px] font-medium transition-colors ${
            isCompleted
              ? 'bg-emerald-500/15 text-emerald-700 hover:bg-emerald-500/25 dark:text-emerald-400'
              : 'bg-yellow-500/15 text-yellow-700 hover:bg-yellow-500/25 dark:text-yellow-300'
          } ${onScrollToTask ? 'cursor-pointer' : ''}`}
          onClick={(e) => {
            e.stopPropagation();
            onScrollToTask?.(taskId);
          }}
        >
          {depTask ? formatTaskDisplayLabel(depTask) : `#${deriveTaskDisplayId(taskId)}`}
        </button>
      </TooltipTrigger>
      <TooltipContent side="bottom">{label}</TooltipContent>
    </Tooltip>
  );
};

const TruncatedTitle = ({
  text,
  className,
}: {
  text: string;
  className?: string;
}): React.JSX.Element => (
  <h5
    className={`line-clamp-2 text-xs font-medium text-[var(--color-text)] ${className ?? ''}`}
    title={text}
  >
    {text}
  </h5>
);

const CancelTaskButton = ({
  taskId,
  onConfirm,
}: {
  taskId: string;
  onConfirm: (taskId: string) => void;
}): React.JSX.Element => {
  const { t } = useAppTranslation('team');
  const [open, setOpen] = useState(false);

  return (
    <Popover open={open} onOpenChange={setOpen}>
      <Tooltip>
        <TooltipTrigger asChild>
          <PopoverTrigger asChild>
            <Button
              variant="destructive"
              size="icon"
              className="size-6 rounded-full shadow-sm"
              aria-label={t('kanban.taskCard.cancelTask', { taskId })}
              onClick={(e) => e.stopPropagation()}
            >
              <XCircle size={11} />
            </Button>
          </PopoverTrigger>
        </TooltipTrigger>
        <TooltipContent side="top">{t('kanban.taskCard.cancel')}</TooltipContent>
      </Tooltip>
      {open ? (
        <PopoverContent
          className="w-56 p-3"
          side="top"
          align="start"
          onClick={(e) => e.stopPropagation()}
        >
          <p className="mb-3 text-xs text-[var(--color-text-secondary)]">
            {t('kanban.taskCard.moveBackToTodoConfirm')}
          </p>
          <div className="flex gap-2">
            <Button
              variant="destructive"
              size="sm"
              className="flex-1"
              onClick={() => {
                setOpen(false);
                onConfirm(taskId);
              }}
            >
              {t('kanban.taskCard.confirm')}
            </Button>
            <Button variant="outline" size="sm" className="flex-1" onClick={() => setOpen(false)}>
              {t('kanban.taskCard.keep')}
            </Button>
          </div>
        </PopoverContent>
      ) : null}
    </Popover>
  );
};

interface TaskActionIconButtonProps {
  label: string;
  icon: React.ReactNode;
  onClick: (e: React.MouseEvent<HTMLButtonElement>) => void;
  className: string;
  variant?: 'outline' | 'ghost' | 'destructive';
  disabled?: boolean;
}

const TaskActionIconButton = ({
  label,
  icon,
  onClick,
  className,
  variant = 'outline',
  disabled = false,
}: TaskActionIconButtonProps): React.JSX.Element => (
  <Button
    variant={variant}
    size="icon"
    className={`size-6 shrink-0 rounded-full shadow-sm ${className}`}
    aria-label={label}
    title={label}
    onClick={onClick}
    disabled={disabled}
  >
    {icon}
  </Button>
);

interface TaskMetaActionsProps {
  taskId: string;
  unreadCount: number;
  commentCount: number;
  pulseKey: number;
  canOpenChanges: boolean;
  changesNeedAttention: boolean;
  onViewChanges?: (taskId: string) => void;
  onDeleteTask?: (taskId: string) => void;
}

const TaskMetaActions = memo(function TaskMetaActions({
  taskId,
  unreadCount,
  commentCount,
  pulseKey,
  canOpenChanges,
  changesNeedAttention,
  onViewChanges,
  onDeleteTask,
}: TaskMetaActionsProps): React.JSX.Element {
  const { t } = useAppTranslation('team');

  return (
    <>
      {canOpenChanges && onViewChanges ? (
        <TaskActionIconButton
          label={
            changesNeedAttention
              ? t('kanban.taskCard.changesNeedAttention')
              : t('kanban.taskCard.changes')
          }
          icon={<FileCode className="size-2.5" />}
          variant="ghost"
          className={
            changesNeedAttention
              ? 'text-amber-400 hover:bg-amber-500/10 hover:text-amber-300'
              : 'text-sky-400 hover:bg-sky-500/10 hover:text-sky-300'
          }
          onClick={(e) => {
            e.stopPropagation();
            onViewChanges(taskId);
          }}
        />
      ) : null}
      <UnreadCommentsBadge
        unreadCount={unreadCount}
        totalCount={commentCount}
        pulseKey={pulseKey}
      />
      {onDeleteTask ? (
        <TaskActionIconButton
          label={t('kanban.taskCard.deleteTask')}
          icon={<Trash2 size={11} />}
          variant="ghost"
          className="text-red-400 hover:bg-red-500/10 hover:text-red-300"
          onClick={(e) => {
            e.stopPropagation();
            onDeleteTask(taskId);
          }}
        />
      ) : null}
    </>
  );
});

interface TaskPrimaryActionsProps {
  taskId: string;
  columnId: KanbanColumnId;
  isReviewManual: boolean;
  onRequestReview: (taskId: string) => void;
  onApprove: (taskId: string) => void;
  onRequestChanges: (taskId: string) => void;
  onMoveBackToDone: (taskId: string) => void;
  onStartTask: (taskId: string) => void;
  onCompleteTask: (taskId: string) => void;
  onCancelTask: (taskId: string) => void;
}

const TaskPrimaryActions = memo(function TaskPrimaryActions({
  taskId,
  columnId,
  isReviewManual,
  onRequestReview,
  onApprove,
  onRequestChanges,
  onMoveBackToDone,
  onStartTask,
  onCompleteTask,
  onCancelTask,
}: TaskPrimaryActionsProps): React.JSX.Element {
  const { t } = useAppTranslation('team');

  return (
    <div className="flex min-w-0 flex-nowrap gap-2">
      {columnId === 'todo' ? (
        <>
          <TaskActionIconButton
            label={t('kanban.taskCard.start')}
            icon={<Play size={11} />}
            className="border-emerald-500/40 text-emerald-400 hover:bg-emerald-500/10 hover:text-emerald-300"
            onClick={(e) => {
              e.stopPropagation();
              onStartTask(taskId);
            }}
          />
          <TaskActionIconButton
            label={t('kanban.taskCard.complete')}
            icon={<CheckCircle2 size={11} />}
            className="border-emerald-500/40 text-emerald-400 hover:bg-emerald-500/10 hover:text-emerald-300"
            onClick={(e) => {
              e.stopPropagation();
              onCompleteTask(taskId);
            }}
          />
        </>
      ) : null}

      {columnId === 'in_progress' ? (
        <>
          <TaskActionIconButton
            label={t('kanban.taskCard.complete')}
            icon={<CheckCircle2 size={11} />}
            className="border-emerald-500/40 text-emerald-400 hover:bg-emerald-500/10 hover:text-emerald-300"
            onClick={(e) => {
              e.stopPropagation();
              onCompleteTask(taskId);
            }}
          />
          <CancelTaskButton taskId={taskId} onConfirm={onCancelTask} />
        </>
      ) : null}

      {columnId === 'done' ? (
        <>
          <TaskActionIconButton
            label={t('kanban.taskCard.approve')}
            icon={<CheckCircle2 size={11} />}
            className="border-emerald-500/40 text-emerald-400 hover:bg-emerald-500/10 hover:text-emerald-300"
            onClick={(e) => {
              e.stopPropagation();
              onApprove(taskId);
            }}
          />
          <TaskActionIconButton
            label={t('kanban.taskCard.requestReview')}
            icon={<Eye size={11} />}
            className="border-violet-500/40 text-violet-400 hover:bg-violet-500/10 hover:text-violet-300"
            onClick={(e) => {
              e.stopPropagation();
              onRequestReview(taskId);
            }}
          />
        </>
      ) : null}

      {columnId === 'review' ? (
        <div className="flex min-w-0 flex-1 flex-col gap-1.5">
          {isReviewManual ? (
            <div className="whitespace-nowrap text-[11px] text-[var(--color-text-muted)]">
              {t('kanban.taskCard.manualReview')}
            </div>
          ) : null}
          <div className="flex flex-wrap items-center gap-2">
            <TaskActionIconButton
              label={t('kanban.taskCard.approve')}
              icon={<CheckCircle2 size={11} />}
              className="border-emerald-500/40 text-emerald-400 hover:bg-emerald-500/10 hover:text-emerald-300"
              onClick={(e) => {
                e.stopPropagation();
                onApprove(taskId);
              }}
            />
            <TaskActionIconButton
              label={t('kanban.taskCard.requestChanges')}
              icon={<FilePenLine size={11} />}
              variant="destructive"
              className="bg-red-500/90 text-white hover:bg-red-500"
              onClick={(e) => {
                e.stopPropagation();
                onRequestChanges(taskId);
              }}
            />
          </div>
        </div>
      ) : null}

      {columnId === 'approved' ? (
        <TaskActionIconButton
          label="Disapprove"
          icon={<RotateCcw size={11} />}
          className="border-amber-500/40 text-amber-400 hover:bg-amber-500/10 hover:text-amber-300"
          onClick={(e) => {
            e.stopPropagation();
            onMoveBackToDone(taskId);
          }}
        />
      ) : null}
    </div>
  );
});

export const KanbanTaskCard = memo(
  function KanbanTaskCard({
    task,
    teamName,
    columnId,
    kanbanTaskState,
    hasReviewers,
    compact,
    taskMap,
    memberColorMap,
    hasLiveTaskLogs = false,
    onRequestReview,
    onApprove,
    onRequestChanges,
    onMoveBackToDone,
    onStartTask,
    onCompleteTask,
    onCancelTask,
    onScrollToTask,
    onTaskClick,
    onViewChanges,
    onDeleteTask,
  }: KanbanTaskCardProps): React.JSX.Element {
    const { t } = useAppTranslation('team');
    const { isLight } = useTheme();
    const unreadCount = useUnreadCommentCount(teamName, task.id, task.comments);
    const commentPulseTaskKey = `${teamName}/${task.id}`;
    const comments = task.comments ?? EMPTY_TASK_COMMENTS;
    const commentCount = comments.length;
    const [commentPulse, syncCommentPulse] = useReducer(
      syncCommentPulseState,
      { taskKey: commentPulseTaskKey, comments },
      ({ taskKey, comments: initialComments }) => createCommentPulseState(taskKey, initialComments)
    );
    const visibleCommentPulseKey =
      commentPulse.taskKey === commentPulseTaskKey ? commentPulse.pulseKey : 0;
    const blockedByIds = task.blockedBy?.filter((id) => id.length > 0) ?? [];
    const blocksIds = task.blocks?.filter((id) => id.length > 0) ?? [];
    const hasBlockedBy = blockedByIds.length > 0;
    const hasBlocks = blocksIds.length > 0;
    const shouldHighlightBlocked = hasBlockedBy && columnId !== 'done' && columnId !== 'approved';
    const cardSurfaceClass = isLight ? 'bg-white' : 'bg-[var(--color-surface-raised)]';

    const taskChangeRequestOptions = useMemo(() => buildTaskChangeRequestOptions(task), [task]);
    const canDisplay = useMemo(
      () => canDisplayTaskChangesForOptions(taskChangeRequestOptions) && !!onViewChanges,
      [taskChangeRequestOptions, onViewChanges]
    );

    const effectiveReviewer = (kanbanTaskState?.reviewer ?? task.reviewer ?? '').trim();
    const isReviewManual = columnId === 'review' && !hasReviewers && effectiveReviewer.length === 0;
    const canOpenChanges =
      canDisplay &&
      (task.changePresence === 'has_changes' || task.changePresence === 'needs_attention');
    const changesNeedAttention = task.changePresence === 'needs_attention';

    useEffect(() => {
      syncCommentPulse({ taskKey: commentPulseTaskKey, comments });
    }, [commentCount, commentPulseTaskKey, comments]);

    return (
      <div
        data-task-id={task.id}
        className={`kanban-task-card relative cursor-pointer rounded-md border px-1.5 py-3 hover:border-[var(--color-border-emphasis)] ${
          shouldHighlightBlocked
            ? `border-yellow-500/30 ${cardSurfaceClass}`
            : `border-[var(--color-border)] ${cardSurfaceClass}`
        }`}
        role="button"
        tabIndex={0}
        onClick={() => onTaskClick?.(task)}
        onKeyDown={(e) => {
          if (e.key === 'Enter' || e.key === ' ') {
            e.preventDefault();
            onTaskClick?.(task);
          }
        }}
      >
        <span className="absolute left-[3px] top-[2px] flex max-w-[calc(100%-72px)] items-center gap-1 text-[9px] leading-none text-[var(--color-text-muted)]">
          <span className="truncate">{formatTaskDisplayLabel(task)}</span>
          {hasLiveTaskLogs ? (
            <span aria-label={t('kanban.taskCard.taskLogsActive')} className="inline-flex">
              <OngoingIndicator size="sm" title={t('kanban.taskCard.newTaskLogsArriving')} />
            </span>
          ) : null}
        </span>
        {task.owner ? (
          <span className="absolute right-[6px] top-[2px]">
            <MemberBadge name={task.owner} color={memberColorMap.get(task.owner)} size="xs" />
          </span>
        ) : null}
        <div className="mb-2 pt-[11px]">
          {!compact && <TruncatedTitle text={task.subject} className="min-w-0" />}
          {task.needsClarification ? (
            <span
              className={`mt-1 inline-flex items-center gap-1 rounded-full px-1.5 py-0.5 text-[10px] font-medium ${
                task.needsClarification === 'user'
                  ? 'bg-red-500/15 text-red-400'
                  : 'bg-blue-500/15 text-blue-600 dark:text-blue-400'
              }`}
            >
              <HelpCircle size={10} />
              {task.needsClarification === 'user'
                ? t('kanban.taskCard.awaitingUser')
                : t('kanban.taskCard.awaitingLead')}
            </span>
          ) : null}
          {isTeamTaskNeedsFixActionable(task) ? (
            <span
              className={`mt-1 inline-flex items-center rounded-full px-1.5 py-0.5 text-[10px] font-medium ${REVIEW_STATE_DISPLAY.needsFix.bg} ${REVIEW_STATE_DISPLAY.needsFix.text}`}
            >
              {REVIEW_STATE_DISPLAY.needsFix.label}
            </span>
          ) : null}
          {compact && <TruncatedTitle text={task.subject} className="mt-1" />}
        </div>

        {hasBlockedBy ? (
          <div className="mb-2 flex flex-wrap items-center gap-1">
            <span className="inline-flex items-center gap-0.5 text-[10px] text-yellow-700 dark:text-yellow-300">
              <ArrowLeftFromLine size={10} />
              {t('kanban.taskCard.blockedBy')}
            </span>
            {blockedByIds.map((id) => (
              <DependencyBadge
                key={id}
                taskId={id}
                taskMap={taskMap}
                onScrollToTask={onScrollToTask}
              />
            ))}
          </div>
        ) : null}

        {hasBlocks ? (
          <div className="mb-2 flex flex-wrap items-center gap-1">
            <span className="inline-flex items-center gap-0.5 text-[10px] text-blue-600 dark:text-blue-400">
              <ArrowRightFromLine size={10} />
              {t('kanban.taskCard.blocks')}
            </span>
            {blocksIds.map((id) => (
              <DependencyBadge
                key={id}
                taskId={id}
                taskMap={taskMap}
                onScrollToTask={onScrollToTask}
              />
            ))}
          </div>
        ) : null}

        <div className="flex items-center justify-between gap-2">
          <TaskPrimaryActions
            taskId={task.id}
            columnId={columnId}
            isReviewManual={isReviewManual}
            onRequestReview={onRequestReview}
            onApprove={onApprove}
            onRequestChanges={onRequestChanges}
            onMoveBackToDone={onMoveBackToDone}
            onStartTask={onStartTask}
            onCompleteTask={onCompleteTask}
            onCancelTask={onCancelTask}
          />

          <div className="flex shrink-0 flex-nowrap items-center gap-1.5">
            <TaskMetaActions
              taskId={task.id}
              unreadCount={unreadCount}
              commentCount={commentCount}
              pulseKey={visibleCommentPulseKey}
              canOpenChanges={canOpenChanges}
              changesNeedAttention={changesNeedAttention}
              onViewChanges={onViewChanges}
              onDeleteTask={onDeleteTask}
            />
          </div>
        </div>
      </div>
    );
  },
  (prev, next) =>
    getTaskCardSignature(prev.task) === getTaskCardSignature(next.task) &&
    prev.teamName === next.teamName &&
    prev.columnId === next.columnId &&
    areKanbanTaskStatesEqual(prev.kanbanTaskState, next.kanbanTaskState) &&
    prev.hasReviewers === next.hasReviewers &&
    prev.compact === next.compact &&
    areTaskMapDependenciesEqual(prev.task, next.task, prev.taskMap, next.taskMap) &&
    prev.memberColorMap === next.memberColorMap &&
    prev.hasLiveTaskLogs === next.hasLiveTaskLogs &&
    prev.onRequestReview === next.onRequestReview &&
    prev.onApprove === next.onApprove &&
    prev.onRequestChanges === next.onRequestChanges &&
    prev.onMoveBackToDone === next.onMoveBackToDone &&
    prev.onStartTask === next.onStartTask &&
    prev.onCompleteTask === next.onCompleteTask &&
    prev.onCancelTask === next.onCancelTask &&
    prev.onScrollToTask === next.onScrollToTask &&
    prev.onTaskClick === next.onTaskClick &&
    prev.onViewChanges === next.onViewChanges &&
    prev.onDeleteTask === next.onDeleteTask
);
