/**
 * Task pill-shaped node rendering.
 * NEW — not from agent-flow. Custom renderer for our task nodes.
 */

import type { GraphNode } from '../ports/types';
import { COLORS, getTaskStatusColor, getReviewStateColor } from '../constants/colors';
import { TASK_PILL, MIN_VISIBLE_OPACITY, ANIM } from '../constants/canvas-constants';
import { truncateText } from './draw-misc';
import { drawPillShell, drawPillStackLayer } from './draw-pill-shell';
import { hexWithAlpha } from './render-cache';
import type { KanbanZoneInfo } from '../layout/kanbanLayout';

/**
 * Draw all task nodes as pill-shaped cards.
 */
export function drawTasks(
  ctx: CanvasRenderingContext2D,
  nodes: GraphNode[],
  time: number,
  selectedId: string | null,
  hoveredId: string | null,
  focusNodeIds?: ReadonlySet<string> | null,
  zoom = 1
): void {
  const simplify = zoom < 0.2;
  for (const node of nodes) {
    if (node.kind !== 'task') continue;

    const opacity = getTaskOpacity(node, focusNodeIds);
    if (opacity < MIN_VISIBLE_OPACITY) continue;

    const x = node.x ?? 0;
    const y = node.y ?? 0;
    const isSelected = node.id === selectedId;
    const isHovered = node.id === hoveredId;

    ctx.save();
    ctx.globalAlpha = opacity;

    if (simplify) {
      drawTaskPillLod(ctx, x, y, node, isSelected, isHovered);
    } else {
      drawTaskPill(ctx, x, y, node, time, isSelected, isHovered);
    }

    ctx.restore();
  }
}

// ─── Private ────────────────────────────────────────────────────────────────

function getTaskOpacity(node: GraphNode, focusNodeIds?: ReadonlySet<string> | null): number {
  if (node.taskStatus === 'deleted') return 0;
  if (focusNodeIds && !focusNodeIds.has(node.id)) return 0.25;
  return 1;
}

function drawTaskPill(
  ctx: CanvasRenderingContext2D,
  x: number,
  y: number,
  node: GraphNode,
  time: number,
  isSelected: boolean,
  isHovered: boolean
): void {
  const w = TASK_PILL.width;
  const h = TASK_PILL.height;
  const r = TASK_PILL.borderRadius;
  const halfW = w / 2;
  const halfH = h / 2;

  const statusColor = getTaskStatusColor(node.taskStatus);
  const reviewColor = getReviewStateColor(node.reviewState);

  ctx.save();
  ctx.translate(x, y);

  if (node.isOverflowStack) {
    drawOverflowStack(ctx, halfW, halfH, r, node, isSelected, isHovered);
    ctx.restore();
    return;
  }

  // Pulse only for active work — completed + approved = static
  const needsAttention =
    (node.taskStatus === 'in_progress' && node.reviewState !== 'approved') ||
    node.reviewState === 'review' ||
    node.reviewState === 'needsFix' ||
    node.needsClarification != null;
  const isFinished = node.taskStatus === 'completed' || node.reviewState === 'approved';
  const breathe =
    needsAttention && !isFinished
      ? 1 + ANIM.breathe.activeAmp * Math.sin(time * ANIM.breathe.activeSpeed)
      : 1;
  const scale = breathe;

  ctx.scale(scale, scale);

  // Shadow — stronger for attention tasks, red for blocked
  ctx.shadowColor = node.isBlocked
    ? hexWithAlpha(COLORS.edgeBlocking, 0.3)
    : hexWithAlpha(statusColor, 0.25);
  ctx.shadowBlur = needsAttention || node.isBlocked ? 12 : 4;

  // Background fill
  drawPillShell(ctx, {
    width: w,
    height: h,
    radius: r,
    fillStyle: isSelected
      ? COLORS.cardBgSelected
      : isHovered
        ? 'rgba(15, 20, 40, 0.7)'
        : COLORS.cardBg,
    borderColor: node.isBlocked
      ? hexWithAlpha(COLORS.edgeBlocking, isSelected ? 0.9 : 0.7)
      : hexWithAlpha(statusColor, isSelected ? 0.8 : 0.5),
    borderWidth: node.isBlocked ? (isSelected ? 2.5 : 1.8) : isSelected ? 2 : 1,
    shadowColor: node.isBlocked
      ? hexWithAlpha(COLORS.edgeBlocking, 0.3)
      : hexWithAlpha(statusColor, 0.25),
    shadowBlur: needsAttention || node.isBlocked ? 12 : 4,
    accentColor: node.isBlocked ? hexWithAlpha(COLORS.edgeBlocking, 0.6) : undefined,
  });

  // Review state overlay border — pulsing for review/needsFix, STATIC for approved
  if (reviewColor !== 'transparent') {
    ctx.beginPath();
    ctx.roundRect(-halfW - 1, -halfH - 1, w + 2, h + 2, r + 1);
    const reviewAlpha = node.reviewState === 'approved' ? 0.6 : 0.5 + 0.3 * Math.sin(time * 3);
    ctx.strokeStyle = hexWithAlpha(reviewColor, reviewAlpha);
    ctx.lineWidth = 1.5;
    ctx.stroke();
  }

  // Clarification warning indicator
  if (node.needsClarification) {
    const pulseAlpha = 0.4 + 0.4 * Math.sin(time * 4);
    ctx.beginPath();
    ctx.roundRect(-halfW - 2, -halfH - 2, w + 4, h + 4, r + 2);
    ctx.strokeStyle = hexWithAlpha(COLORS.error, pulseAlpha);
    ctx.lineWidth = 1;
    ctx.stroke();
  }

  // Subject (main title — large)
  if (node.sublabel) {
    ctx.font = `bold ${TASK_PILL.idFontSize}px sans-serif`;
    ctx.textAlign = 'left';
    ctx.textBaseline = 'middle';
    ctx.fillStyle = COLORS.textPrimary;
    const textX = -halfW + 10;
    const hasReviewChip =
      node.reviewState !== 'approved' &&
      (node.reviewMode === 'manual' || (node.reviewMode === 'assigned' && !!node.reviewerName));
    const maxW = hasReviewChip ? w - 64 : w - 18;
    const subject = truncateText(ctx, node.sublabel, maxW, ctx.font);
    ctx.fillText(subject, textX, -4);
  }

  // Display ID (secondary — small)
  const displayId = node.displayId ?? node.label;
  ctx.font = `${TASK_PILL.subjectFontSize}px monospace`;
  ctx.textAlign = 'left';
  ctx.textBaseline = 'middle';
  ctx.fillStyle = COLORS.textDim;
  ctx.fillText(displayId, -halfW + 10, 8);

  // Approved badge: checkmark at right side
  if (node.reviewState === 'approved') {
    ctx.font = 'bold 11px sans-serif';
    ctx.textAlign = 'right';
    ctx.textBaseline = 'middle';
    ctx.fillStyle = COLORS.reviewApproved;
    ctx.fillText('\u2713', halfW - 8, 0); // ✓
  }

  if (
    node.reviewState !== 'approved' &&
    (node.reviewMode === 'manual' || (node.reviewMode === 'assigned' && node.reviewerName))
  ) {
    drawReviewChip(ctx, halfW, -halfH, node);
  }

  // Comment count badge — on the bottom-right border edge, 1.5x bigger
  if (node.totalCommentCount && node.totalCommentCount > 0) {
    const badgeX = halfW - 6;
    const badgeY = halfH;

    // Speech bubble background
    const bw = 20;
    const bh = 15;
    ctx.fillStyle = hexWithAlpha('#aaeeff', 0.85);
    ctx.beginPath();
    ctx.roundRect(badgeX - bw / 2, badgeY - bh / 2, bw, bh, 3);
    ctx.fill();
    // Tail pointing up-left
    ctx.beginPath();
    ctx.moveTo(badgeX - 5, badgeY + bh / 2);
    ctx.lineTo(badgeX - 9, badgeY + bh / 2 + 5);
    ctx.lineTo(badgeX - 1, badgeY + bh / 2);
    ctx.closePath();
    ctx.fill();

    // Total count inside bubble
    ctx.font = 'bold 10px monospace';
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.fillStyle = '#0a0f1e';
    ctx.fillText(String(node.totalCommentCount), badgeX, badgeY + 0.5);

    // Unread count badge (blue circle, top-right of bubble)
    if (node.unreadCommentCount && node.unreadCommentCount > 0) {
      const dotX = badgeX + bw / 2 + 1;
      const dotY = badgeY - bh / 2 - 1;
      ctx.fillStyle = '#3b82f6';
      ctx.beginPath();
      ctx.arc(dotX, dotY, 7, 0, Math.PI * 2);
      ctx.fill();
      ctx.font = 'bold 8px monospace';
      ctx.fillStyle = '#ffffff';
      ctx.textAlign = 'center';
      ctx.textBaseline = 'middle';
      ctx.fillText(String(node.unreadCommentCount), dotX, dotY + 0.5);
    }
  }

  ctx.restore();
}

function drawTaskPillLod(
  ctx: CanvasRenderingContext2D,
  x: number,
  y: number,
  node: GraphNode,
  isSelected: boolean,
  isHovered: boolean
): void {
  const w = TASK_PILL.width;
  const h = TASK_PILL.height;
  const r = TASK_PILL.borderRadius;
  const halfW = w / 2;
  const halfH = h / 2;

  const statusColor = getTaskStatusColor(node.taskStatus);

  ctx.save();
  ctx.translate(x, y);

  if (node.isOverflowStack) {
    drawOverflowStack(ctx, halfW, halfH, r, node, isSelected, isHovered);
    ctx.restore();
    return;
  }

  ctx.beginPath();
  ctx.roundRect(-halfW, -halfH, w, h, r);
  ctx.fillStyle = isSelected
    ? COLORS.cardBgSelected
    : isHovered
      ? 'rgba(15, 20, 40, 0.78)'
      : COLORS.cardBg;
  ctx.fill();
  ctx.strokeStyle = node.isBlocked
    ? hexWithAlpha(COLORS.edgeBlocking, isSelected ? 0.85 : 0.65)
    : hexWithAlpha(statusColor, isSelected ? 0.8 : 0.55);
  ctx.lineWidth = node.isBlocked ? (isSelected ? 2.2 : 1.5) : isSelected ? 2 : 1;
  ctx.stroke();

  if (node.isBlocked) {
    ctx.fillStyle = hexWithAlpha(COLORS.edgeBlocking, 0.6);
    ctx.beginPath();
    ctx.roundRect(-halfW, -halfH, 4, h, [r, 0, 0, r]);
    ctx.fill();
  }

  ctx.restore();
}

function drawOverflowStack(
  ctx: CanvasRenderingContext2D,
  halfW: number,
  halfH: number,
  r: number,
  node: GraphNode,
  isSelected: boolean,
  isHovered: boolean
): void {
  for (const [offset, alpha] of [
    [6, 0.18],
    [3, 0.28],
  ] as const) {
    drawPillStackLayer(ctx, {
      width: TASK_PILL.width,
      height: TASK_PILL.height,
      radius: r,
      offsetX: offset,
      offsetY: -offset,
      fillColor: '#334155',
      fillAlpha: alpha,
    });
  }

  drawPillShell(ctx, {
    width: TASK_PILL.width,
    height: TASK_PILL.height,
    radius: r,
    fillStyle: isSelected
      ? COLORS.cardBgSelected
      : isHovered
        ? 'rgba(15, 20, 40, 0.78)'
        : COLORS.cardBg,
    borderColor: node.isBlocked
      ? hexWithAlpha(COLORS.edgeBlocking, isSelected ? 0.85 : 0.65)
      : hexWithAlpha(COLORS.taskPending, isSelected ? 0.85 : 0.55),
    borderWidth: node.isBlocked ? (isSelected ? 2.4 : 1.5) : isSelected ? 2 : 1,
    accentColor: node.isBlocked ? hexWithAlpha(COLORS.edgeBlocking, 0.6) : undefined,
  });

  ctx.font = 'bold 12px sans-serif';
  ctx.textAlign = 'left';
  ctx.textBaseline = 'middle';
  ctx.fillStyle = COLORS.textPrimary;
  ctx.fillText(node.label, -halfW + 12, -2);

  ctx.font = '7px monospace';
  ctx.fillStyle = COLORS.textDim;
  ctx.fillText('more tasks', -halfW + 12, 10);
}

function drawReviewChip(
  ctx: CanvasRenderingContext2D,
  halfW: number,
  halfH: number,
  node: GraphNode
): void {
  const chipText = node.reviewMode === 'manual' ? 'REV' : (node.reviewerName ?? 'REV');
  const chipColor = node.reviewMode === 'manual' ? '#8b5cf6' : (node.reviewerColor ?? '#38bdf8');
  const chipX = halfW - 44;
  const chipY = halfH + 10;
  const chipW = 34;
  const chipH = 12;

  ctx.beginPath();
  ctx.roundRect(chipX, chipY, chipW, chipH, 6);
  ctx.fillStyle = hexWithAlpha(chipColor, 0.2);
  ctx.fill();
  ctx.strokeStyle = hexWithAlpha(chipColor, 0.55);
  ctx.lineWidth = 1;
  ctx.stroke();

  ctx.font = 'bold 7px monospace';
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle';
  ctx.fillStyle = hexWithAlpha(chipColor, 0.95);
  ctx.fillText(
    chipText.length > 8 ? `${chipText.slice(0, 7)}…` : chipText,
    chipX + chipW / 2,
    chipY + chipH / 2 + 0.5
  );

  if (node.changePresence === 'has_changes') {
    ctx.beginPath();
    ctx.arc(chipX + chipW + 4, chipY + chipH / 2, 2.5, 0, Math.PI * 2);
    ctx.fillStyle = '#38bdf8';
    ctx.fill();
  }
}

/**
 * Draw kanban column headers above task columns.
 */
export function drawColumnHeaders(
  ctx: CanvasRenderingContext2D,
  zones: KanbanZoneInfo[],
  zoom = 1
): void {
  if (zoom < 0.22) return;
  for (const zone of zones) {
    // Section header for unassigned tasks — larger, centered above all columns
    if (zone.ownerId === '__unassigned__') {
      ctx.font = 'bold 10px monospace';
      ctx.textAlign = 'center';
      ctx.textBaseline = 'bottom';
      ctx.fillStyle = hexWithAlpha(COLORS.taskPending, 0.5);
      const labelY = (zone.headers[0]?.y ?? zone.ownerY + 60) - 16;
      ctx.fillText('Unassigned', zone.ownerX, labelY);

      // Overflow badge
      for (const header of zone.headers) {
        if (header.overflowCount > 0) {
          ctx.font = '7px monospace';
          ctx.textAlign = 'center';
          ctx.textBaseline = 'top';
          ctx.fillStyle = hexWithAlpha(header.color, 0.45);
          ctx.fillText(`+${header.overflowCount} more`, header.x, header.overflowY + 4);
        }
      }
      continue;
    }

    for (const header of zone.headers) {
      ctx.font = 'bold 8px monospace';
      ctx.textAlign = 'center';
      ctx.textBaseline = 'bottom';
      ctx.fillStyle = hexWithAlpha(header.color, 0.6);
      ctx.fillText(header.label, header.x, header.y - 2);

      // Overflow badge: "+N more"
      if (header.overflowCount > 0) {
        const badgeText = `+${header.overflowCount} more`;
        ctx.font = '7px monospace';
        ctx.textAlign = 'center';
        ctx.textBaseline = 'top';
        ctx.fillStyle = hexWithAlpha(header.color, 0.45);
        ctx.fillText(badgeText, header.x, header.overflowY + 4);
      }
    }
  }
}
