'use client';

import { useMemo, useState, useRef, useEffect } from 'react';
import { Target, Zap, Clock, LayoutGrid, TrendingUp, Users, Wrench, X, ChevronRight, MessageSquare, Star, Maximize2 } from 'lucide-react';
import type { OpportunityMatrix as OpportunityMatrixType, OpportunityItem, OpportunitySpan } from '../types';
import { ReviewModal } from '../tables/ReviewModal';
import { DOMAIN_COLORS, COMPLEXITY_LABELS } from '../types';

interface OpportunityMatrixProps {
  matrix: OpportunityMatrixType | null;
  onSubcodeClick?: (subcode: string) => void;
}

interface QuadrantProps {
  title: string;
  icon: React.ReactNode;
  items: OpportunityItem[];
  bgColor: string;
  borderColor: string;
  textColor: string;
  iconBg: string;
  onItemClick?: (item: OpportunityItem) => void;
  onItemHover?: (item: OpportunityItem | null) => void;
  selectedItem?: OpportunityItem | null;
}

type ConnectorSide = 'top' | 'bottom' | 'left' | 'right';

interface LabelPosition {
  x: number;  // Label center X
  y: number;  // Label center Y
  connector: ConnectorSide;  // Which side the edge connects to
  nodeX: number;
  nodeY: number;
}

// Get the connector point on the label edge based on connector side
function getConnectorPoint(pos: LabelPosition): { x: number; y: number } {
  const halfW = LABEL_WIDTH / 2;
  const halfH = LABEL_HEIGHT / 2;

  switch (pos.connector) {
    case 'top':
      return { x: pos.x, y: pos.y - halfH };
    case 'bottom':
      return { x: pos.x, y: pos.y + halfH };
    case 'left':
      return { x: pos.x - halfW, y: pos.y };
    case 'right':
      return { x: pos.x + halfW, y: pos.y };
  }
}

// Determine the best connector side based on angle from node to label
function getConnectorSide(nodeX: number, nodeY: number, labelX: number, labelY: number): ConnectorSide {
  const angle = Math.atan2(labelY - nodeY, labelX - nodeX) * (180 / Math.PI);

  // Angle ranges for each connector:
  // Right: -45 to 45 (label is to the right of node, connect on left side of label)
  // Bottom: 45 to 135 (label is below node, connect on top of label)
  // Left: 135 to 180 or -180 to -135 (label is to the left, connect on right side)
  // Top: -135 to -45 (label is above node, connect on bottom of label)

  if (angle >= -45 && angle < 45) {
    return 'left';  // Label is to the right, connect on its left side
  } else if (angle >= 45 && angle < 135) {
    return 'top';   // Label is below, connect on its top
  } else if (angle >= -135 && angle < -45) {
    return 'bottom'; // Label is above, connect on its bottom
  } else {
    return 'right'; // Label is to the left, connect on its right side
  }
}

// Estimated label dimensions in percentage units
const LABEL_WIDTH = 18; // ~18% width for truncated names (up to 14 chars)
const LABEL_HEIGHT = 6; // ~6% height

// Check if two rectangles overlap (with padding)
function rectsOverlap(
  r1: { x: number; y: number; w: number; h: number },
  r2: { x: number; y: number; w: number; h: number },
  padding = 1
): boolean {
  return !(
    r1.x + r1.w + padding < r2.x ||
    r2.x + r2.w + padding < r1.x ||
    r1.y + r1.h + padding < r2.y ||
    r2.y + r2.h + padding < r1.y
  );
}

// Get label bounding box based on center position
function getLabelRect(pos: { x: number; y: number }) {
  const w = LABEL_WIDTH;
  const h = LABEL_HEIGHT;
  const x = pos.x - w / 2;
  const y = pos.y - h / 2;
  return { x, y, w, h };
}

// Check if two line segments intersect
function linesIntersect(
  p1: { x: number; y: number },
  p2: { x: number; y: number },
  p3: { x: number; y: number },
  p4: { x: number; y: number }
): boolean {
  const ccw = (A: { x: number; y: number }, B: { x: number; y: number }, C: { x: number; y: number }) => {
    return (C.y - A.y) * (B.x - A.x) > (B.y - A.y) * (C.x - A.x);
  };

  // Check if segments share an endpoint (not a real crossing)
  const samePoint = (a: { x: number; y: number }, b: { x: number; y: number }) =>
    Math.abs(a.x - b.x) < 0.5 && Math.abs(a.y - b.y) < 0.5;

  if (samePoint(p1, p3) || samePoint(p1, p4) || samePoint(p2, p3) || samePoint(p2, p4)) {
    return false;
  }

  return ccw(p1, p3, p4) !== ccw(p2, p3, p4) && ccw(p1, p2, p3) !== ccw(p1, p2, p4);
}

interface Cluster {
  centerX: number;
  centerY: number;
  radius: number;
  nodeIndices: number[];
}

// Find clusters of nearby nodes using distance threshold
function findClusters(nodes: Array<{ x: number; y: number }>, threshold: number): Cluster[] {
  const visited = new Set<number>();
  const clusters: Cluster[] = [];

  for (let i = 0; i < nodes.length; i++) {
    if (visited.has(i)) continue;

    // BFS to find all connected nodes
    const clusterIndices: number[] = [];
    const queue = [i];

    while (queue.length > 0) {
      const current = queue.shift()!;
      if (visited.has(current)) continue;
      visited.add(current);
      clusterIndices.push(current);

      // Find neighbors within threshold
      for (let j = 0; j < nodes.length; j++) {
        if (visited.has(j)) continue;
        const dist = Math.hypot(nodes[j].x - nodes[current].x, nodes[j].y - nodes[current].y);
        if (dist < threshold) {
          queue.push(j);
        }
      }
    }

    // Calculate cluster center and radius
    if (clusterIndices.length > 0) {
      const clusterNodes = clusterIndices.map((idx) => nodes[idx]);
      const centerX = clusterNodes.reduce((sum, n) => sum + n.x, 0) / clusterNodes.length;
      const centerY = clusterNodes.reduce((sum, n) => sum + n.y, 0) / clusterNodes.length;

      // Radius is max distance from center + padding for labels
      let maxDist = 0;
      for (const n of clusterNodes) {
        const dist = Math.hypot(n.x - centerX, n.y - centerY);
        maxDist = Math.max(maxDist, dist);
      }
      const radius = maxDist + 15; // Padding for labels

      clusters.push({ centerX, centerY, radius, nodeIndices: clusterIndices });
    }
  }

  return clusters;
}

// Generate candidate positions for a node based on cluster membership
function generateCandidatePositions(
  node: { x: number; y: number },
  cluster: Cluster | null
): Array<{ x: number; y: number }> {
  const positions: Array<{ x: number; y: number }> = [];

  if (cluster && cluster.nodeIndices.length > 1) {
    // For clustered nodes: project outward from cluster center through the node
    const baseAngle = Math.atan2(node.y - cluster.centerY, node.x - cluster.centerX);

    // Primary position: on the cluster circle boundary
    // Plus variations around that angle for fine-tuning
    const angleVariations = [0, -0.25, 0.25, -0.5, 0.5, -0.75, 0.75];
    const distanceVariations = [cluster.radius, cluster.radius + 6, cluster.radius - 4];

    for (const distVar of distanceVariations) {
      for (const angleVar of angleVariations) {
        const angle = baseAngle + angleVar;
        const x = cluster.centerX + Math.cos(angle) * distVar;
        const y = cluster.centerY + Math.sin(angle) * distVar * 0.85;
        positions.push({ x, y });
      }
    }
  } else {
    // For isolated nodes: use clock positions around the node
    const distances = [12, 16];
    for (const dist of distances) {
      for (let hour = 0; hour < 12; hour++) {
        const angle = (hour * 30 - 90) * (Math.PI / 180);
        const dx = Math.cos(angle) * dist;
        const dy = Math.sin(angle) * dist * 0.75;
        positions.push({ x: node.x + dx, y: node.y + dy });
      }
    }
  }

  return positions;
}

// Calculate optimal label positions to avoid overlaps
function calculateLabelPositions(items: OpportunityItem[]): LabelPosition[] {
  if (items.length === 0) return [];

  // Convert items to node positions
  const nodes = items.map((item) => ({
    x: 15 + item.x * 70,
    y: 30 + item.y * 55,
    subcode: item.subcode,
  }));

  // Find clusters of nearby nodes
  const clusters = findClusters(nodes, 15);

  // Map each node to its cluster (if any)
  const nodeToCluster = new Map<number, Cluster>();
  for (const cluster of clusters) {
    if (cluster.nodeIndices.length > 1) {
      for (const idx of cluster.nodeIndices) {
        nodeToCluster.set(idx, cluster);
      }
    }
  }

  // Initialize result array with default positions
  const result: LabelPosition[] = nodes.map((node) => ({
    x: node.x + 12,
    y: node.y,
    connector: 'left' as ConnectorSide,
    nodeX: node.x,
    nodeY: node.y,
  }));

  // Track which labels have been placed
  const placedLabels: LabelPosition[] = [];

  // Sort: process clustered nodes first (by cluster size desc), then by angle within cluster
  const sortedIndices = nodes
    .map((_, i) => i)
    .sort((a, b) => {
      const clusterA = nodeToCluster.get(a);
      const clusterB = nodeToCluster.get(b);
      const sizeA = clusterA?.nodeIndices.length ?? 0;
      const sizeB = clusterB?.nodeIndices.length ?? 0;

      if (sizeA !== sizeB) return sizeB - sizeA; // Larger clusters first

      // Within same cluster, sort by angle from center
      if (clusterA && clusterA === clusterB) {
        const angleA = Math.atan2(nodes[a].y - clusterA.centerY, nodes[a].x - clusterA.centerX);
        const angleB = Math.atan2(nodes[b].y - clusterA.centerY, nodes[b].x - clusterA.centerX);
        return angleA - angleB;
      }

      return nodes[a].y - nodes[b].y;
    });

  for (const idx of sortedIndices) {
    const node = nodes[idx];
    const cluster = nodeToCluster.get(idx) ?? null;
    const candidates = generateCandidatePositions(node, cluster);

    let bestPosition: LabelPosition | null = null;
    let minScore = Infinity;

    for (const candidate of candidates) {
      // Determine which connector side based on angle from node to label center
      const connector = getConnectorSide(node.x, node.y, candidate.x, candidate.y);

      const pos: LabelPosition = {
        x: candidate.x,
        y: candidate.y,
        connector,
        nodeX: node.x,
        nodeY: node.y,
      };

      // Check bounds (keep label within quadrant)
      const rect = getLabelRect(pos);
      if (rect.x < 3 || rect.x + rect.w > 97 || rect.y < 22 || rect.y + rect.h > 93) {
        continue;
      }

      // Calculate score (lower is better)
      let score = 0;

      // Penalty for overlapping with placed labels
      for (const placed of placedLabels) {
        const placedRect = getLabelRect(placed);
        if (rectsOverlap(rect, placedRect, 2)) {
          const overlapX = Math.max(0, Math.min(rect.x + rect.w, placedRect.x + placedRect.w) - Math.max(rect.x, placedRect.x));
          const overlapY = Math.max(0, Math.min(rect.y + rect.h, placedRect.y + placedRect.h) - Math.max(rect.y, placedRect.y));
          score += (overlapX * overlapY) * 10;
        }
      }

      // Penalty for overlapping with other nodes (dots)
      for (const otherNode of nodes) {
        if (otherNode === node) continue;
        const nodeRect = { x: otherNode.x - 3, y: otherNode.y - 3, w: 6, h: 6 };
        if (rectsOverlap(rect, nodeRect, 0)) {
          score += 100;
        }
      }

      // Heavy penalty for leader line crossing other leader lines (using connector points)
      const connectorPoint = getConnectorPoint(pos);
      for (const placed of placedLabels) {
        const placedConnector = getConnectorPoint(placed);
        if (linesIntersect(
          { x: node.x, y: node.y },
          connectorPoint,
          { x: placed.nodeX, y: placed.nodeY },
          placedConnector
        )) {
          score += 200;
        }
      }

      // Small penalty for distance from node (prefer closer positions)
      const dist = Math.hypot(candidate.x - node.x, candidate.y - node.y);
      score += dist * 0.05;

      if (score < minScore) {
        minScore = score;
        bestPosition = pos;
      }

      if (score < 0.1) break;
    }

    if (bestPosition) {
      result[idx] = bestPosition;
    }

    placedLabels.push(result[idx]);
  }

  return result;
}

function Quadrant({
  title,
  icon,
  items,
  bgColor,
  borderColor,
  textColor,
  iconBg,
  onItemClick,
  onItemHover,
  selectedItem,
}: QuadrantProps) {
  // Calculate optimized label positions
  const labelPositions = useMemo(() => calculateLabelPositions(items), [items]);

  return (
    <div className={`${bgColor} rounded-lg border ${borderColor} relative overflow-hidden`}>
      {/* Header */}
      <div className="absolute top-2 left-2 right-2 flex items-center gap-1.5 z-10">
        <div className={`p-1 ${iconBg} rounded`}>{icon}</div>
        <span className={`text-xs font-bold ${textColor}`}>{title}</span>
        <span className="ml-auto text-xs font-semibold text-gray-500 bg-white/60 px-1.5 py-0.5 rounded">
          {items.length}
        </span>
      </div>

      {/* Positioned items with dots and leader lines */}
      {items.length === 0 ? (
        <div className="absolute inset-0 flex items-center justify-center">
          <span className="text-xs text-gray-400 italic">None</span>
        </div>
      ) : (
        <svg className="absolute inset-0 w-full h-full pointer-events-none z-10">
          {items.map((item, index) => {
            const pos = labelPositions[index];
            if (!pos) return null;

            // Get the connector point on the label edge
            const connector = getConnectorPoint(pos);
            const isSelected = selectedItem?.subcode === item.subcode;

            return (
              <g key={`${item.subcode}-${index}`}>
                {/* Leader line from node to label connector */}
                <line
                  x1={`${pos.nodeX}%`}
                  y1={`${pos.nodeY}%`}
                  x2={`${connector.x}%`}
                  y2={`${connector.y}%`}
                  stroke="currentColor"
                  strokeWidth={isSelected ? 2 : 1}
                  opacity={isSelected ? 0.6 : 0.3}
                />
                {/* Dot at node position */}
                <circle
                  cx={`${pos.nodeX}%`}
                  cy={`${pos.nodeY}%`}
                  r={isSelected ? 5 : 4}
                  fill="currentColor"
                  opacity={isSelected ? 0.8 : 0.6}
                />
              </g>
            );
          })}
        </svg>
      )}

      {/* Clickable labels */}
      {items.length > 0 && items.map((item, index) => {
        const pos = labelPositions[index];
        if (!pos) return null;

        // Truncate long names for display
        const displayName = item.name.length > 14 ? item.name.slice(0, 12) + '…' : item.name;
        const isSelected = selectedItem?.subcode === item.subcode;

        return (
          <button
            key={`label-${item.subcode}-${index}`}
            onClick={() => onItemClick?.(item)}
            onMouseEnter={() => onItemHover?.(item)}
            onMouseLeave={() => onItemHover?.(null)}
            className={`absolute px-1.5 py-0.5 text-[10px] font-medium rounded ${textColor} transition-all shadow-sm border z-20 whitespace-nowrap ${
              isSelected
                ? 'bg-white scale-110 ring-2 ring-current/40 border-current/40'
                : 'bg-white/95 hover:bg-white hover:scale-105 border-current/20'
            }`}
            style={{
              left: `${pos.x}%`,
              top: `${pos.y}%`,
              transform: 'translate(-50%, -50%)',
            }}
          >
            {displayName}
          </button>
        );
      })}
    </div>
  );
}

// Hover preview card
function HoverCard({ item, position }: { item: OpportunityItem; position: { x: number; y: number } }) {
  return (
    <div
      className="fixed z-50 bg-white rounded-lg shadow-xl border border-gray-200 p-3 w-56 pointer-events-none"
      style={{
        left: position.x + 10,
        top: position.y - 10,
        transform: 'translateY(-100%)',
      }}
    >
      <div className="flex items-start gap-2 mb-2">
        <div
          className="w-2 h-2 rounded-full mt-1.5 flex-shrink-0"
          style={{ backgroundColor: DOMAIN_COLORS[item.domain] || '#6b7280' }}
        />
        <div>
          <div className="font-semibold text-sm text-gray-900">{item.name}</div>
          <div className="text-xs text-gray-500">{item.domain_name}</div>
        </div>
      </div>

      <div className="space-y-1.5">
        <div className="flex justify-between text-xs">
          <span className="text-gray-500">Negative</span>
          <span className="font-medium text-red-600">{item.negative_pct}%</span>
        </div>
        <div className="w-full bg-gray-100 rounded-full h-1.5">
          <div
            className="bg-red-500 h-1.5 rounded-full"
            style={{ width: `${item.negative_pct}%` }}
          />
        </div>
        {item.rating_impact && (
          <div className="flex justify-between text-xs pt-1">
            <span className="text-gray-500">Rating impact</span>
            <span className="font-medium text-green-600">+{item.rating_impact}</span>
          </div>
        )}
      </div>

      <div className="mt-2 pt-2 border-t border-gray-100 text-xs text-gray-400">
        Click for details →
      </div>
    </div>
  );
}

// Render stars for rating
function RatingStars({ rating }: { rating: number }) {
  return (
    <div className="flex items-center gap-0.5">
      {[1, 2, 3, 4, 5].map((star) => (
        <Star
          key={star}
          className={`w-3 h-3 ${
            star <= rating
              ? 'text-amber-400 fill-amber-400'
              : 'text-gray-300 fill-gray-300'
          }`}
        />
      ))}
    </div>
  );
}

// Normalize text for matching (handles spacing differences around punctuation)
function normalizeForMatching(text: string): string {
  return text
    .toLowerCase()
    .replace(/\s+([.,!?;:])/g, '$1')  // Remove space before punctuation
    .replace(/([.,!?;:])\s+/g, '$1 ') // Normalize space after punctuation
    .replace(/\s+/g, ' ')              // Multiple spaces to single
    .trim();
}

// Find span in review using normalized matching, return original indices
function findSpanInReview(reviewText: string, spanText: string): { start: number; end: number } | null {
  // Try exact match first (case-insensitive)
  const lowerReview = reviewText.toLowerCase();
  const lowerSpan = spanText.toLowerCase();
  let idx = lowerReview.indexOf(lowerSpan);
  if (idx !== -1) {
    return { start: idx, end: idx + spanText.length };
  }

  // Try normalized matching
  const normReview = normalizeForMatching(reviewText);
  const normSpan = normalizeForMatching(spanText);
  const normIdx = normReview.indexOf(normSpan);

  if (normIdx === -1) return null;

  // Map normalized index back to original text
  // Walk through original text, tracking normalized position
  let origIdx = 0;
  let normPos = 0;
  const targetNormStart = normIdx;
  const targetNormEnd = normIdx + normSpan.length;
  let origStart = -1;
  let origEnd = -1;

  while (origIdx <= reviewText.length && normPos <= normReview.length) {
    if (normPos === targetNormStart) origStart = origIdx;
    if (normPos === targetNormEnd) {
      origEnd = origIdx;
      break;
    }

    if (origIdx < reviewText.length) {
      const origChar = reviewText[origIdx];
      const normChar = normReview[normPos];

      // Skip characters that were removed in normalization
      if (origChar.toLowerCase() === normChar) {
        origIdx++;
        normPos++;
      } else if (/\s/.test(origChar)) {
        origIdx++; // Skip extra whitespace in original
      } else {
        origIdx++;
        normPos++;
      }
    } else {
      break;
    }
  }

  if (origStart !== -1 && origEnd === -1) {
    origEnd = reviewText.length;
  }

  return origStart !== -1 ? { start: origStart, end: origEnd } : null;
}

// Highlight span text within review text
function HighlightedReviewText({
  reviewText,
  spanText,
  maxLength = 150,
  expanded = false,
}: {
  reviewText: string;
  spanText: string;
  maxLength?: number;
  expanded?: boolean;
}) {
  // Find the span within the review using fuzzy matching
  const match = findSpanInReview(reviewText, spanText);

  if (!match) {
    // Span not found, just show truncated review
    const displayText = expanded ? reviewText : reviewText.slice(0, maxLength);
    return (
      <span>
        {displayText}
        {!expanded && reviewText.length > maxLength && '...'}
      </span>
    );
  }

  const spanIndex = match.start;
  const spanEnd = match.end;

  // If span covers most of the review (>80%), highlight the whole thing
  const spanCoverage = (spanEnd - spanIndex) / reviewText.length;
  if (spanCoverage > 0.8) {
    const displayText = expanded ? reviewText : reviewText.slice(0, maxLength);
    return (
      <mark className="bg-yellow-200 text-gray-900 px-0.5 rounded">
        {displayText}
        {!expanded && reviewText.length > maxLength && '...'}
      </mark>
    );
  }

  // Calculate display window around the span
  let displayStart = 0;
  let displayEnd = reviewText.length;

  if (!expanded) {
    // Show context around the span
    const contextBefore = 40;
    const contextAfter = 60;
    displayStart = Math.max(0, spanIndex - contextBefore);
    displayEnd = Math.min(reviewText.length, spanEnd + contextAfter);

    // Adjust to not cut words
    if (displayStart > 0) {
      const spaceIndex = reviewText.indexOf(' ', displayStart);
      if (spaceIndex !== -1 && spaceIndex < spanIndex) {
        displayStart = spaceIndex + 1;
      }
    }
    if (displayEnd < reviewText.length) {
      const spaceIndex = reviewText.lastIndexOf(' ', displayEnd);
      if (spaceIndex !== -1 && spaceIndex > spanEnd) {
        displayEnd = spaceIndex;
      }
    }
  }

  const before = reviewText.slice(displayStart, spanIndex);
  const highlight = reviewText.slice(spanIndex, spanEnd);
  const after = reviewText.slice(spanEnd, displayEnd);

  return (
    <span>
      {displayStart > 0 && '...'}
      {before}
      <mark className="bg-yellow-200 text-gray-900 px-0.5 rounded">{highlight}</mark>
      {after}
      {!expanded && displayEnd < reviewText.length && '...'}
    </span>
  );
}

// Google-style review card for customer feedback
function SpanCard({
  span,
  onViewReview
}: {
  span: OpportunitySpan;
  onViewReview?: (reviewId: string, spanId: string) => void;
}) {
  const [expanded, setExpanded] = useState(false);
  const reviewText = span.review_text || span.span_text;
  const isLongReview = reviewText.length > 150;

  return (
    <div className="bg-white rounded-lg p-3 border border-gray-200 hover:shadow-sm transition-shadow group">
      {/* Header: Stars + Date + Expand button */}
      <div className="flex items-center gap-2 mb-2">
        {span.rating && <RatingStars rating={span.rating} />}
        {span.review_date && (
          <span className="text-xs text-gray-500">{span.review_date}</span>
        )}
        {span.review_id && onViewReview && (
          <button
            onClick={() => onViewReview(span.review_id!, span.span_id)}
            className="ml-auto p-1 rounded hover:bg-gray-100 opacity-0 group-hover:opacity-100 transition-opacity"
            title="View full review"
          >
            <Maximize2 className="w-3.5 h-3.5 text-gray-400 hover:text-gray-600" />
          </button>
        )}
      </div>

      {/* Review text with highlighted span */}
      <p className="text-sm text-gray-700 leading-relaxed">
        {span.review_text ? (
          <HighlightedReviewText
            reviewText={span.review_text}
            spanText={span.span_text}
            expanded={expanded}
          />
        ) : (
          <span>"{span.span_text}"</span>
        )}
        {isLongReview && !expanded && (
          <button
            onClick={() => setExpanded(true)}
            className="ml-1 text-blue-600 hover:text-blue-800 font-medium"
          >
            More
          </button>
        )}
        {expanded && isLongReview && (
          <button
            onClick={() => setExpanded(false)}
            className="ml-1 text-blue-600 hover:text-blue-800 font-medium"
          >
            Less
          </button>
        )}
      </p>
    </div>
  );
}

// Detail panel component
function DetailPanel({
  item,
  onClose,
  onViewReview,
}: {
  item: OpportunityItem;
  onClose: () => void;
  onViewReview?: (reviewId: string, spanId: string) => void;
}) {
  return (
    <div className="bg-white rounded-xl border-2 border-gray-200 shadow-md p-4 h-full flex flex-col overflow-hidden">
      {/* Header */}
      <div className="flex items-start justify-between mb-4 flex-shrink-0">
        <div className="flex items-start gap-3">
          <div
            className="w-3 h-3 rounded-full mt-1 flex-shrink-0"
            style={{ backgroundColor: DOMAIN_COLORS[item.domain] || '#6b7280' }}
          />
          <div>
            <h3 className="font-bold text-gray-900">{item.name}</h3>
            <div className="text-sm text-gray-500">{item.domain_name} · {item.subcode}</div>
          </div>
        </div>
        <button
          onClick={onClose}
          className="p-1 hover:bg-gray-100 rounded transition-colors"
        >
          <X className="w-4 h-4 text-gray-400" />
        </button>
      </div>

      {/* Metrics */}
      <div className="grid grid-cols-2 gap-3 mb-4 flex-shrink-0">
        <div className="bg-red-50 rounded-lg p-3">
          <div className="text-xs text-red-600 mb-1">Negative Sentiment</div>
          <div className="text-2xl font-bold text-red-700">{item.negative_pct}%</div>
          <div className="text-xs text-red-500">{item.span_count} mentions</div>
        </div>
        {item.rating_impact && (
          <div className="bg-green-50 rounded-lg p-3">
            <div className="text-xs text-green-600 mb-1 flex items-center gap-1">
              <TrendingUp className="w-3 h-3" />
              If Fixed
            </div>
            <div className="text-2xl font-bold text-green-700">+{item.rating_impact}</div>
            <div className="text-xs text-green-500">rating points</div>
          </div>
        )}
      </div>

      {/* Complexity */}
      <div className="flex items-center gap-2 mb-4 p-2 bg-gray-50 rounded-lg flex-shrink-0">
        <Wrench className="w-4 h-4 text-gray-400" />
        <span className="text-sm text-gray-600">Complexity:</span>
        <span className={`text-sm font-medium px-2 py-0.5 rounded ${
          item.complexity === 'simple' ? 'bg-green-100 text-green-700' :
          item.complexity === 'medium' ? 'bg-yellow-100 text-yellow-700' :
          'bg-red-100 text-red-700'
        }`}>
          {COMPLEXITY_LABELS[item.complexity] || item.complexity}
        </span>
      </div>

      {/* Customer Feedback Spans (evidence of the problem) - expands to fill available space */}
      {item.spans && item.spans.length > 0 && (
        <div className="mb-4 flex-1 min-h-0 flex flex-col">
          <div className="text-xs font-medium text-gray-500 mb-2 uppercase tracking-wide flex items-center gap-1.5 flex-shrink-0">
            <MessageSquare className="w-3 h-3" />
            Customer Feedback ({item.spans.length})
          </div>
          <div className="overflow-y-auto space-y-2 pr-1 flex-1 min-h-[100px]">
            {item.spans.map((span) => (
              <SpanCard
                key={span.span_id}
                span={span}
                onViewReview={onViewReview}
              />
            ))}
          </div>
        </div>
      )}

      {/* Solution */}
      {item.solution && (
        <div className="mb-4 flex-shrink-0">
          <div className="text-xs font-medium text-gray-500 mb-2 uppercase tracking-wide">
            💡 Suggested Solution
          </div>
          <p className="text-sm text-gray-700 leading-relaxed bg-blue-50 p-3 rounded-lg border border-blue-100">
            {item.solution}
          </p>
        </div>
      )}

      {/* Owner */}
      {item.owner && (
        <div className="flex items-center gap-2 p-2 bg-purple-50 rounded-lg mb-4 flex-shrink-0">
          <Users className="w-4 h-4 text-purple-500" />
          <span className="text-sm text-purple-700">
            <span className="text-purple-500">Assign to:</span> {item.owner}
          </span>
        </div>
      )}

      {/* CTA */}
      <button className="w-full flex items-center justify-center gap-2 bg-indigo-600 hover:bg-indigo-700 text-white py-2 px-4 rounded-lg transition-colors text-sm font-medium flex-shrink-0">
        View Related Reviews
        <ChevronRight className="w-4 h-4" />
      </button>
    </div>
  );
}

/**
 * 2x2 Opportunity Matrix visualization.
 * Shows issues categorized by frequency vs complexity with coordinate positioning.
 */
export function OpportunityMatrix({ matrix, onSubcodeClick }: OpportunityMatrixProps) {
  const [selectedItem, setSelectedItem] = useState<OpportunityItem | null>(null);
  const [hoveredItem, setHoveredItem] = useState<OpportunityItem | null>(null);
  const [mousePos, setMousePos] = useState({ x: 0, y: 0 });
  const [gridSize, setGridSize] = useState<number | null>(null);
  const matrixRef = useRef<HTMLDivElement>(null);
  const gridContainerRef = useRef<HTMLDivElement>(null);
  const componentRef = useRef<HTMLDivElement>(null);
  const [reviewModal, setReviewModal] = useState<{ reviewId: string; spanId: string } | null>(null);

  // Calculate grid size based on available space
  useEffect(() => {
    const updateGridSize = () => {
      // Calculate max available height for the grid (viewport - margins - header - labels - legend)
      const maxAvailableHeight = window.innerHeight - 300;

      // Get component width, then subtract detail panel min (280px) + y-axis label (40px) + gaps (32px)
      const componentWidth = componentRef.current?.offsetWidth ?? window.innerWidth * 0.6;
      const maxAvailableWidth = componentWidth - 280 - 40 - 32; // detail panel min + y-label + padding/gaps

      // Grid must be square - use the smaller of available height or width
      const size = Math.min(maxAvailableHeight, maxAvailableWidth);
      setGridSize(Math.max(250, size));
    };

    // Delay initial calculation to allow layout to settle
    const timeoutId = setTimeout(updateGridSize, 50);
    window.addEventListener('resize', updateGridSize);

    // Also observe component width changes
    if (componentRef.current) {
      const resizeObserver = new ResizeObserver(updateGridSize);
      resizeObserver.observe(componentRef.current);
      return () => {
        clearTimeout(timeoutId);
        window.removeEventListener('resize', updateGridSize);
        resizeObserver.disconnect();
      };
    }

    return () => {
      clearTimeout(timeoutId);
      window.removeEventListener('resize', updateGridSize);
    };
  }, []);

  if (!matrix) {
    return null;
  }

  const totalItems =
    matrix.quick_wins.length +
    matrix.critical.length +
    matrix.nice_to_have.length +
    matrix.strategic.length;

  if (totalItems === 0) {
    return null;
  }

  const handleItemClick = (item: OpportunityItem) => {
    setSelectedItem(item);
    onSubcodeClick?.(item.subcode);
  };

  const handleItemHover = (item: OpportunityItem | null) => {
    setHoveredItem(item);
  };

  const handleMouseMove = (e: React.MouseEvent) => {
    setMousePos({ x: e.clientX, y: e.clientY });
  };

  return (
    <div
      ref={componentRef}
      className="bg-white rounded-xl p-4 shadow-md border-2 border-gray-200"
      style={{
        maxHeight: 'calc(100vh - 120px)',
      }}
      onMouseMove={handleMouseMove}
    >
      <div className="flex items-center gap-2 mb-3 flex-shrink-0">
        <LayoutGrid className="w-5 h-5 text-indigo-600" />
        <h3 className="text-lg font-bold text-gray-900">Opportunity Matrix</h3>
        <span className="ml-auto text-xs text-gray-500 bg-gray-100 px-2 py-1 rounded-full">
          {totalItems} opportunities
        </span>
      </div>

      {/* Main layout: Matrix + Detail Panel */}
      <div className="flex gap-4 items-start">
        {/* Matrix section */}
        <div ref={matrixRef} className="flex-shrink-0">
          {/* Matrix with L-shaped label area */}
          <div className="flex">
        {/* Y-axis label column - height matches grid */}
        <div
          className="flex items-center justify-center w-8 mr-2"
          style={{ height: gridSize ?? 300 }}
        >
          <span className="-rotate-90 text-sm text-gray-600 font-semibold whitespace-nowrap">
            Effort →
          </span>
        </div>

        {/* Main matrix area */}
        <div ref={gridContainerRef} className="flex flex-col">
          {/* Matrix with arrows */}
          <div className="relative">
            {/* Coordinate axes - L-shaped from bottom-left origin */}
            <svg
              className="absolute pointer-events-none overflow-visible"
              style={{
                zIndex: 5,
                width: gridSize ?? 300,
                height: gridSize ?? 300,
                left: 12,
                top: 0,
              }}
              viewBox="0 0 100 100"
              preserveAspectRatio="none"
            >
              <defs>
                {/* Horizontal arrowhead - points right */}
                <marker
                  id="arrowhead-right"
                  markerWidth="6"
                  markerHeight="6"
                  refX="5"
                  refY="3"
                  orient="0"
                  markerUnits="strokeWidth"
                >
                  <path d="M0,0 L6,3 L0,6 Z" fill="#6b7280" />
                </marker>
                {/* Vertical arrowhead - points up (pre-rotated triangle) */}
                <marker
                  id="arrowhead-up"
                  markerWidth="6"
                  markerHeight="6"
                  refX="3"
                  refY="1"
                  orient="0"
                  markerUnits="strokeWidth"
                >
                  <path d="M0,6 L3,0 L6,6 Z" fill="#6b7280" />
                </marker>
              </defs>
              {/* X-axis (horizontal - frequency increases right) */}
              <line
                x1="-1" y1="102"
                x2="99" y2="102"
                stroke="#6b7280"
                strokeWidth="2.5"
                markerEnd="url(#arrowhead-right)"
                vectorEffect="non-scaling-stroke"
              />
              {/* Y-axis (vertical - effort increases up) */}
              <line
                x1="-1" y1="102"
                x2="-1" y2="1"
                stroke="#6b7280"
                strokeWidth="2.5"
                markerEnd="url(#arrowhead-up)"
                vectorEffect="non-scaling-stroke"
              />
            </svg>

            <div
              className="relative ml-3 mb-3"
              style={{
                width: gridSize ?? 300,
                height: gridSize ?? 300,
                maxWidth: 'calc(100% - 12px)',
              }}
            >
              {/* Unified coordinate grid overlay */}
              <svg
                className="absolute top-0 left-0 pointer-events-none"
                style={{ zIndex: 2, width: '100%', height: '100%' }}
                viewBox="0 0 100 100"
                preserveAspectRatio="none"
              >
                {/* Vertical grid lines */}
                {[12.5, 25, 37.5, 50, 62.5, 75, 87.5].map((x) => (
                  <line
                    key={`grid-v-${x}`}
                    x1={x}
                    y1="0"
                    x2={x}
                    y2="100"
                    stroke="#9ca3af"
                    strokeWidth="1"
                    opacity="0.15"
                    strokeDasharray="3 3"
                    vectorEffect="non-scaling-stroke"
                  />
                ))}
                {/* Horizontal grid lines */}
                {[12.5, 25, 37.5, 50, 62.5, 75, 87.5].map((y) => (
                  <line
                    key={`grid-h-${y}`}
                    x1="0"
                    y1={y}
                    x2="100"
                    y2={y}
                    stroke="#9ca3af"
                    strokeWidth="1"
                    opacity="0.15"
                    strokeDasharray="3 3"
                    vectorEffect="non-scaling-stroke"
                  />
                ))}
              </svg>

              <div className="grid grid-cols-2 grid-rows-2 gap-2 w-full h-full">
              {/* Top Row: Simple solutions */}
              <Quadrant
              title="Nice to Have"
              icon={<Clock className="w-3 h-3 text-gray-600" />}
              items={matrix.nice_to_have}
              bgColor="bg-gray-50"
              borderColor="border-gray-200"
              textColor="text-gray-700"
              iconBg="bg-gray-200"
              onItemClick={handleItemClick}
              onItemHover={handleItemHover}
              selectedItem={selectedItem}
            />
            <Quadrant
              title="Quick Wins"
              icon={<Zap className="w-3 h-3 text-green-600" />}
              items={matrix.quick_wins}
              bgColor="bg-green-50"
              borderColor="border-green-200"
              textColor="text-green-700"
              iconBg="bg-green-200"
              onItemClick={handleItemClick}
              onItemHover={handleItemHover}
              selectedItem={selectedItem}
            />

            {/* Bottom Row: Complex solutions */}
            <Quadrant
              title="Strategic"
              icon={<Target className="w-3 h-3 text-purple-600" />}
              items={matrix.strategic}
              bgColor="bg-purple-50"
              borderColor="border-purple-200"
              textColor="text-purple-700"
              iconBg="bg-purple-200"
              onItemClick={handleItemClick}
              onItemHover={handleItemHover}
              selectedItem={selectedItem}
            />
            <Quadrant
              title="Critical"
              icon={<Target className="w-3 h-3 text-red-600" />}
              items={matrix.critical}
              bgColor="bg-red-50"
              borderColor="border-red-200"
              textColor="text-red-700"
              iconBg="bg-red-200"
              onItemClick={handleItemClick}
              onItemHover={handleItemHover}
              selectedItem={selectedItem}
            />
              </div>
            </div>
          </div>

          {/* X-axis label row */}
          <div className="flex justify-center mt-4 flex-shrink-0">
            <span className="text-sm text-gray-600 font-semibold">
              Frequency →
            </span>
          </div>

          {/* Legend */}
          <div className="mt-3 pt-3 border-t border-gray-100 flex flex-wrap gap-3 text-xs text-gray-500 flex-shrink-0">
            <div className="flex items-center gap-1">
              <div className="w-3 h-3 bg-green-200 rounded" />
              <span>Quick Wins</span>
            </div>
            <div className="flex items-center gap-1">
              <div className="w-3 h-3 bg-red-200 rounded" />
              <span>Critical</span>
            </div>
            <div className="flex items-center gap-1">
              <div className="w-3 h-3 bg-purple-200 rounded" />
              <span>Strategic</span>
            </div>
            <div className="flex items-center gap-1">
              <div className="w-3 h-3 bg-gray-200 rounded" />
              <span>Nice to Have</span>
            </div>
          </div>
        </div>
        </div>
        </div>

        {/* Detail Panel - expands horizontally, height matches grid + labels */}
        <div
          className="flex-1 min-w-[280px] overflow-hidden"
          style={{ height: gridSize ? gridSize + 80 : 'auto' }}
        >
          {selectedItem ? (
            <DetailPanel
              item={selectedItem}
              onClose={() => setSelectedItem(null)}
              onViewReview={(reviewId, spanId) => setReviewModal({ reviewId, spanId })}
            />
          ) : (
            <div className="bg-gray-50 rounded-xl border-2 border-dashed border-gray-200 p-6 h-full flex flex-col items-center justify-center text-center">
              <div className="w-12 h-12 bg-gray-100 rounded-full flex items-center justify-center mb-3">
                <Target className="w-6 h-6 text-gray-400" />
              </div>
              <h4 className="font-medium text-gray-600 mb-1">Select an opportunity</h4>
              <p className="text-sm text-gray-400 max-w-[200px]">
                Click on any item in the matrix to see details and suggested actions
              </p>
            </div>
          )}
        </div>
      </div>

      {/* Hover Card */}
      {hoveredItem && !selectedItem && (
        <HoverCard item={hoveredItem} position={mousePos} />
      )}

      {/* Review Modal */}
      {reviewModal && (
        <ReviewModal
          reviewId={reviewModal.reviewId}
          highlightSpanId={reviewModal.spanId}
          onClose={() => setReviewModal(null)}
        />
      )}
    </div>
  );
}
