'use client';

import type { CSSProperties } from 'react';
import { useEffect, useMemo, useState } from 'react';
import { useRouter } from 'next/navigation';
import { fetchWithAuth } from '@/lib/auth';
import { useTranslation, type TranslationKey } from '@/lib/i18n';

type IssueStatus = 'open' | 'in_progress' | 'fixed' | string;

interface TriageIssue {
  id: string;
  title: string;
  type: string;
  issue_stage: string;
  severity: string;
  status: IssueStatus;
  github_issue: string;
  fixed_in: string;
  fix_plan: string;
  affects_files: string[];
  found_in_cases: string[];
  found_in_count: number;
  pr_numbers: string[];
  diagnosis_track: string;
  fix_source: string;
  autofix_category: string;
}

interface TriageSummary {
  total: number;
  open: number;
  in_progress: number;
  fixed: number;
  resolved_or_active: number;
  fix_rate: number;
  by_status: Record<string, number>;
  by_type: Record<string, number>;
  by_issue_stage: Record<string, number>;
  by_severity: Record<string, number>;
  by_diagnosis_track: Record<string, number>;
  by_fix_source: Record<string, number>;
  by_autofix_category: Record<string, number>;
}

interface TriageTrendBucket {
  day: string;
  new_issues: number;
  autofix_handled: number;
  manual_handled: number;
  handled_total: number;
  open_backlog: number;
}

type ChartTrendBucket = TriageTrendBucket & {
  label: string;
};

interface TriageTrend {
  range: { start_day: string; end_day: string; timezone: string };
  buckets: TriageTrendBucket[];
  undated: { created: number; handled: number };
  outside_window?: { created: number; handled: number };
}

interface TriageResponse {
  issues: TriageIssue[];
  summary: TriageSummary;
  trend: TriageTrend;
  source_path: string;
  parse_mode: string;
  generated_at: string;
}

const STATUS_OPTIONS = ['all', 'open', 'in_progress', 'fixed'] as const;

const STATUS_META: Record<string, { label: string; color: string; background: string }> = {
  open: { label: 'Open', color: '#b42318', background: '#fff1f3' },
  in_progress: { label: 'In progress', color: '#975a16', background: '#fff7e6' },
  fixed: { label: 'Fixed', color: '#067647', background: '#ecfdf3' },
};

const CATEGORY_META: Record<string, { label: string; color: string; background: string }> = {
  autofixable: { label: 'Autofixable', color: '#175cd3', background: '#eff8ff' },
  manual: { label: 'Manual', color: '#6941c6', background: '#f4f3ff' },
  needs_plan: { label: 'Needs plan', color: '#7a2e0e', background: '#fff6ed' },
  out_of_scope: { label: 'Out of scope', color: '#475467', background: '#f2f4f7' },
  in_progress: { label: 'In progress', color: '#975a16', background: '#fff7e6' },
  fixed: { label: 'Fixed', color: '#067647', background: '#ecfdf3' },
};

const TRACK_META: Record<string, { label: string; color: string; background: string }> = {
  skill_quality: { label: 'Skill quality', color: '#6941c6', background: '#f4f3ff' },
  data_tooling: { label: 'Data & tooling', color: '#175cd3', background: '#eff8ff' },
};

const STAGE_META: Record<string, { label: string; color: string; background: string }> = {
  ux: { label: 'UX', color: '#175cd3', background: '#eff8ff' },
  template: { label: 'Template', color: '#7a2e0e', background: '#fff6ed' },
  intake_context: { label: 'Intake & context', color: '#93370d', background: '#fff4ed' },
  data_retrieval: { label: 'Data retrieval', color: '#047647', background: '#ecfdf3' },
  data_semantics: { label: 'Data semantics', color: '#0e7090', background: '#ecfdff' },
  classification: { label: 'Classification', color: '#6941c6', background: '#f4f3ff' },
  specialized_diagnosis: { label: 'Diagnosis', color: '#3538cd', background: '#eef4ff' },
  recommendation: { label: 'Recommendation', color: '#c01048', background: '#fff1f3' },
};

const STAGE_ORDER = [
  'ux',
  'template',
  'intake_context',
  'data_retrieval',
  'data_semantics',
  'classification',
  'specialized_diagnosis',
  'recommendation',
];

const FIX_SOURCE_META: Record<string, { labelKey: TranslationKey; color: string; background: string }> = {
  autofix: { labelKey: 'adminTriagePilot.fixSourceAutofix', color: '#175cd3', background: '#eff8ff' },
  manual: { labelKey: 'adminTriagePilot.fixSourceManual', color: '#475467', background: '#f2f4f7' },
  unfixed: { labelKey: 'adminTriagePilot.fixSourceUnfixed', color: '#b42318', background: '#fff1f3' },
};

const TREND_SERIES: { key: keyof Pick<TriageTrendBucket, 'new_issues' | 'autofix_handled' | 'manual_handled' | 'open_backlog'>; label: string; color: string; axis: 'flow' | 'backlog' }[] = [
  { key: 'new_issues', label: 'New issues', color: '#12b76a', axis: 'flow' },
  { key: 'autofix_handled', label: 'Autofix handled', color: '#175cd3', axis: 'flow' },
  { key: 'manual_handled', label: 'Manual/other handled', color: '#f59e0b', axis: 'flow' },
  { key: 'open_backlog', label: 'Open backlog', color: '#7c3aed', axis: 'backlog' },
];

const TREND_RANGE_LABEL = 'Last 8 weeks';

type TrendTooltip = {
  day: string;
  label: string;
  value: number;
  color: string;
  x: number;
  y: number;
};

function StatCard({
  label,
  value,
  tone,
  suffix,
  active = false,
  onClick,
}: {
  label: string;
  value: string | number;
  tone: string;
  suffix?: string;
  active?: boolean;
  onClick?: () => void;
}) {
  const Component = onClick ? 'button' : 'div';
  return (
    <Component
      onClick={onClick}
      style={{
        background: '#fff',
        border: active ? '1.5px solid #5c5fbc' : '1px solid #e5e7eb',
        borderRadius: 8,
        padding: '16px 18px',
        minHeight: 98,
        boxShadow: active ? '0 0 0 3px rgba(92,95,188,0.12)' : '0 1px 4px rgba(16,24,40,0.04)',
        cursor: onClick ? 'pointer' : 'default',
        textAlign: 'left',
        width: '100%',
        transition: 'border-color 0.15s, box-shadow 0.15s',
      }}
    >
      <div style={{ fontSize: 12, color: '#667085', fontWeight: 600, textTransform: 'uppercase' }}>
        {label}
      </div>
      <div style={{ display: 'flex', alignItems: 'baseline', gap: 6, marginTop: 12 }}>
        <span style={{ fontSize: 30, lineHeight: 1, fontWeight: 760, color: tone }}>{value}</span>
        {suffix && <span style={{ fontSize: 13, color: '#98a2b3', fontWeight: 600 }}>{suffix}</span>}
      </div>
    </Component>
  );
}

function Badge({ label, color, background }: { label: string; color: string; background: string }) {
  return (
    <span
      style={{
        display: 'inline-flex',
        alignItems: 'center',
        minHeight: 22,
        padding: '3px 8px',
        borderRadius: 6,
        background,
        color,
        fontSize: 12,
        fontWeight: 700,
        whiteSpace: 'nowrap',
      }}
    >
      {label}
    </span>
  );
}

function DistributionBars({
  counts,
  activeValue,
  onSelect,
  labelOverrides,
  meta,
  order,
}: {
  counts: Record<string, number>;
  activeValue?: string;
  onSelect?: (value: string) => void;
  labelOverrides?: Record<string, string>;
  meta?: Record<string, { label: string; color: string }>;
  order?: string[];
}) {
  const rows = Object.entries(counts).sort((a, b) => {
    if (!order) return b[1] - a[1];
    const rank = (name: string) => {
      const index = order.indexOf(name);
      return index === -1 ? 99 : index;
    };
    return rank(a[0]) - rank(b[0]) || b[1] - a[1];
  });
  const max = Math.max(...rows.map(([, count]) => count), 1);

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
      {rows.map(([name, count]) => {
        const active = activeValue === name;
        const itemMeta = meta?.[name];
        const displayName = labelOverrides?.[name] ?? itemMeta?.label ?? TRACK_META[name]?.label ?? CATEGORY_META[name]?.label ?? name;
        return (
          <button
            key={name}
            onClick={() => onSelect?.(name)}
            style={{
              display: 'grid',
              gridTemplateColumns: '132px 1fr 34px',
              gap: 10,
              alignItems: 'center',
              border: active ? '1px solid rgba(92,95,188,0.3)' : '1px solid transparent',
              background: active ? '#f4f3ff' : 'transparent',
              borderRadius: 7,
              padding: '4px 6px',
              cursor: onSelect ? 'pointer' : 'default',
              textAlign: 'left',
            }}
          >
            <div style={{ fontSize: 12, color: '#475467', fontWeight: 700, overflow: 'hidden', textOverflow: 'ellipsis' }}>
              {displayName}
            </div>
            <div style={{ height: 8, background: '#eef2f6', borderRadius: 4, overflow: 'hidden' }}>
              <div
                style={{
                  width: `${(count / max) * 100}%`,
                  height: '100%',
                  background: itemMeta?.color ?? (name === 'UX' ? '#2e90fa' : name === 'TOOLING' ? '#12b76a' : '#7c7fdc'),
                }}
              />
            </div>
            <div style={{ fontSize: 12, color: '#667085', fontWeight: 700, textAlign: 'right' }}>{count}</div>
          </button>
        );
      })}
    </div>
  );
}

function parseTrendDate(day: string) {
  const [year, month, date] = day.split('-').map(Number);
  return new Date(Date.UTC(year, month - 1, date));
}

function formatTrendDate(date: Date) {
  return `${date.getUTCMonth() + 1}/${date.getUTCDate()}`;
}

function getWeekStart(date: Date) {
  const weekStart = new Date(date);
  const mondayOffset = (weekStart.getUTCDay() + 6) % 7;
  weekStart.setUTCDate(weekStart.getUTCDate() - mondayOffset);
  return weekStart;
}

function aggregateTrendBucketsByWeek(buckets: TriageTrendBucket[]): ChartTrendBucket[] {
  const grouped = new Map<string, ChartTrendBucket>();

  for (const bucket of buckets) {
    const day = parseTrendDate(bucket.day);
    const weekStart = getWeekStart(day);
    const weekEnd = new Date(weekStart);
    weekEnd.setUTCDate(weekEnd.getUTCDate() + 6);
    const weekKey = weekStart.toISOString().slice(0, 10);
    const existing = grouped.get(weekKey);

    if (existing) {
      existing.new_issues += bucket.new_issues;
      existing.autofix_handled += bucket.autofix_handled;
      existing.manual_handled += bucket.manual_handled;
      existing.handled_total += bucket.handled_total;
      existing.open_backlog = bucket.open_backlog;
    } else {
      grouped.set(weekKey, {
        ...bucket,
        day: weekKey,
        label: `${formatTrendDate(weekStart)}-${formatTrendDate(weekEnd)}`,
      });
    }
  }

  return [...grouped.values()];
}

function trendValue(bucket: TriageTrendBucket | ChartTrendBucket, key: (typeof TREND_SERIES)[number]['key']) {
  return bucket[key] ?? 0;
}

function buildLogTicks(maxValue: number) {
  const ticks = [0];
  for (let value = 1; value <= maxValue; value *= 2) {
    if (value === 1 || Math.log2(value) % 2 === 0) {
      ticks.push(value);
    }
  }
  if (ticks[ticks.length - 1] < maxValue) {
    const nextPowerOfTwo = 2 ** Math.ceil(Math.log2(maxValue));
    const nextEvenPower = Math.log2(nextPowerOfTwo) % 2 === 0 ? nextPowerOfTwo : nextPowerOfTwo * 2;
    ticks.push(nextEvenPower);
  }
  return ticks;
}

function TriageTrendChart({ trend }: { trend: TriageTrend }) {
  const [tooltip, setTooltip] = useState<TrendTooltip | null>(null);
  const buckets = aggregateTrendBucketsByWeek(trend.buckets);
  const W = 960;
  const H = 260;
  const padL = 46;
  const padR = 18;
  const padT = 20;
  const padB = 38;
  const innerW = W - padL - padR;
  const innerH = H - padT - padB;
  const baselineY = padT + innerH;
  const n = buckets.length;
  const groupW = n > 0 ? innerW / n : innerW;
  const maxV = Math.max(
    1,
    ...buckets.flatMap((bucket) => TREND_SERIES.map((series) => trendValue(bucket, series.key))),
  );
  const yTicks = buildLogTicks(maxV);
  const axisMax = yTicks[yTicks.length - 1] ?? 1;
  const scaleY = (value: number) => {
    const scaled = Math.log2(value + 1) / Math.log2(axisMax + 1);
    return padT + innerH - scaled * innerH;
  };
  const outsideWindow = trend.outside_window ?? { created: 0, handled: 0 };
  const tooltipW = 168;
  const tooltipH = 54;
  const tooltipX = tooltip ? Math.min(Math.max(tooltip.x - tooltipW / 2, padL), W - padR - tooltipW) : 0;
  const tooltipY = tooltip ? Math.max(8, tooltip.y - tooltipH - 14) : 0;
  const linePoints = (series: (typeof TREND_SERIES)[number]) =>
    buckets
      .map((bucket, index) => {
        const x = padL + index * groupW + groupW / 2;
        const y = scaleY(trendValue(bucket, series.key));
        return `${x},${y}`;
      })
      .join(' ');

  if (buckets.length === 0) {
    return (
      <div style={{ background: '#fff', border: '1px solid #e5e7eb', borderRadius: 8, padding: 18, marginBottom: 18 }}>
        <div style={{ fontSize: 13, fontWeight: 780, color: '#101828' }}>Issue trend</div>
        <div style={{ marginTop: 10, color: '#98a2b3', fontSize: 13 }}>No dated issue activity yet.</div>
      </div>
    );
  }

  return (
    <div style={{ background: '#fff', border: '1px solid #e5e7eb', borderRadius: 8, padding: 16, marginBottom: 18 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', gap: 16, alignItems: 'flex-start', marginBottom: 10 }}>
        <div>
          <div style={{ fontSize: 13, fontWeight: 780, color: '#101828' }}>Issue trend</div>
          <div style={{ marginTop: 4, fontSize: 12, color: '#667085', fontWeight: 600 }}>
            {TREND_RANGE_LABEL}, by week ({trend.range.timezone})
          </div>
        </div>
        <div style={{ display: 'flex', gap: 12, flexWrap: 'wrap', justifyContent: 'flex-end' }}>
          {TREND_SERIES.map((series) => (
            <span key={series.key} style={{ display: 'inline-flex', alignItems: 'center', gap: 6, fontSize: 12, color: '#475467', fontWeight: 700 }}>
              <span style={{ width: 9, height: 9, borderRadius: 3, background: series.color }} />
              {series.label}
            </span>
          ))}
        </div>
      </div>
      <svg
        viewBox={`0 0 ${W} ${H}`}
        width="100%"
        height={H}
        role="img"
        aria-label="TriagePilot issue trend chart"
        onMouseLeave={() => setTooltip(null)}
      >
        {yTicks.map((tick) => {
          const y = scaleY(tick);
          return (
            <g key={tick}>
              <line x1={padL} x2={W - padR} y1={y} y2={y} stroke="#eef2f6" />
              <text x={padL - 10} y={y + 4} textAnchor="end" fontSize="11" fill="#98a2b3" fontWeight="700">
                {tick}
              </text>
            </g>
          );
        })}
        {TREND_SERIES.map((series) => (
          <polyline
            key={series.key}
            points={linePoints(series)}
            fill="none"
            stroke={series.color}
            strokeWidth={series.axis === 'backlog' ? '2.8' : '3'}
            strokeLinecap="round"
            strokeLinejoin="round"
            strokeDasharray={series.axis === 'backlog' ? '5 4' : undefined}
          />
        ))}
        {buckets.map((bucket, index) => (
          <g key={bucket.day}>
            {TREND_SERIES.map((series) => {
              const value = trendValue(bucket, series.key);
              const x = padL + index * groupW + groupW / 2;
              const y = scaleY(value);
              const shouldLabel = value > 0 && (series.axis === 'flow' || index === buckets.length - 1);
              const active = tooltip?.day === bucket.label && tooltip.label === series.label;
              const showTooltip = () => setTooltip({ day: bucket.label, label: series.label, value, color: series.color, x, y });
              return (
                <g key={series.key}>
                  <circle cx={x} cy={y} r={active ? 5.5 : 4} fill={series.color} />
                  <circle
                    cx={x}
                    cy={y}
                    r={11}
                    fill="transparent"
                    stroke="transparent"
                    style={{ cursor: 'crosshair' }}
                    tabIndex={0}
                    aria-label={`${bucket.label} ${series.label}: ${value}`}
                    onMouseEnter={showTooltip}
                    onMouseMove={showTooltip}
                    onFocus={showTooltip}
                    onMouseLeave={() => setTooltip(null)}
                    onBlur={() => setTooltip(null)}
                  />
                  {shouldLabel && (
                    <text
                      x={x}
                      y={Math.max(12, y - 10)}
                      textAnchor="middle"
                      fontSize="11"
                      fill={series.color}
                      fontWeight="800"
                    >
                      {value}
                    </text>
                  )}
                </g>
              );
            })}
            <text x={padL + index * groupW + groupW / 2} y={H - 10} textAnchor="middle" fontSize="11" fill="#667085" fontWeight="700">
              {bucket.label}
            </text>
          </g>
        ))}
        <line x1={padL} x2={W - padR} y1={baselineY} y2={baselineY} stroke="#d0d5dd" />
        {tooltip && (
          <g pointerEvents="none">
            <rect
              x={tooltipX}
              y={tooltipY}
              width={tooltipW}
              height={tooltipH}
              rx={8}
              fill="#101828"
              opacity="0.94"
            />
            <circle cx={tooltipX + 14} cy={tooltipY + 17} r={4} fill={tooltip.color} />
            <text x={tooltipX + 24} y={tooltipY + 21} fontSize="12" fill="#f9fafb" fontWeight="800">
              {tooltip.label}
            </text>
            <text x={tooltipX + 14} y={tooltipY + 40} fontSize="12" fill="#d0d5dd" fontWeight="650">
              {tooltip.day}: {tooltip.value}
            </text>
          </g>
        )}
      </svg>
      {(trend.undated.created > 0 || trend.undated.handled > 0 || outsideWindow.created > 0 || outsideWindow.handled > 0) && (
        <div style={{ marginTop: 8, fontSize: 12, color: '#98a2b3', fontWeight: 650 }}>
          Outside this 8-week chart: {outsideWindow.created + trend.undated.created} new,{' '}
          {outsideWindow.handled + trend.undated.handled} handled.
        </div>
      )}
    </div>
  );
}

function IssueTable({
  issues,
  emptyLabel,
  fixSourceLabels,
  fixSourceHeader,
  autofixStateHeader,
}: {
  issues: TriageIssue[];
  emptyLabel: string;
  fixSourceLabels: Record<string, string>;
  fixSourceHeader: string;
  autofixStateHeader: string;
}) {
  if (issues.length === 0) {
    return (
      <div style={{ padding: 28, textAlign: 'center', color: '#98a2b3', fontSize: 14 }}>
        {emptyLabel}
      </div>
    );
  }

  return (
    <div style={{ overflowX: 'auto' }}>
      <table style={{ width: '100%', borderCollapse: 'collapse', minWidth: 860 }}>
        <thead>
          <tr style={{ borderBottom: '1px solid #e5e7eb', background: '#f8fafc' }}>
            <th style={thStyle}>Issue</th>
            <th style={thStyle}>Track</th>
            <th style={thStyle}>Stage</th>
            <th style={thStyle}>Severity</th>
            <th style={thStyle}>Status</th>
            <th style={thStyle}>{fixSourceHeader}</th>
            <th style={thStyle}>{autofixStateHeader}</th>
            <th style={thStyle}>Cases</th>
            <th style={thStyle}>PR</th>
          </tr>
        </thead>
        <tbody>
          {issues.map((issue) => {
            const status = STATUS_META[issue.status] ?? {
              label: issue.status,
              color: '#475467',
              background: '#f2f4f7',
            };
            const category = CATEGORY_META[issue.autofix_category] ?? {
              label: issue.autofix_category,
              color: '#475467',
              background: '#f2f4f7',
            };
            const track = TRACK_META[issue.diagnosis_track] ?? {
              label: issue.diagnosis_track,
              color: '#475467',
              background: '#f2f4f7',
            };
            const stage = STAGE_META[issue.issue_stage] ?? {
              label: issue.issue_stage,
              color: '#475467',
              background: '#f2f4f7',
            };
            const fixSource = FIX_SOURCE_META[issue.fix_source];
            const fixSourceLabel = fixSourceLabels[issue.fix_source] ?? issue.fix_source;
            return (
              <tr key={issue.id} style={{ borderBottom: '1px solid #eef2f6' }}>
                <td style={{ ...tdStyle, maxWidth: 420 }}>
                  <div style={{ display: 'flex', flexDirection: 'column', gap: 5 }}>
                    <a
                      href={issue.github_issue || undefined}
                      target="_blank"
                      rel="noreferrer"
                      style={{
                        color: issue.github_issue ? '#3440a3' : '#101828',
                        fontWeight: 760,
                        fontSize: 13,
                        textDecoration: 'none',
                      }}
                    >
                      {issue.id}
                    </a>
                    <div style={{ color: '#344054', fontSize: 13, lineHeight: 1.45 }}>
                      {issue.title || 'Untitled issue'}
                    </div>
                    {issue.affects_files.length > 0 && (
                      <div
                        title={issue.affects_files.join('\n')}
                        style={{
                          color: '#98a2b3',
                          fontSize: 12,
                          overflow: 'hidden',
                          textOverflow: 'ellipsis',
                          whiteSpace: 'nowrap',
                        }}
                      >
                        {issue.affects_files[0]}
                      </div>
                    )}
                  </div>
                </td>
                <td style={tdStyle}>
                  <Badge label={track.label} color={track.color} background={track.background} />
                </td>
                <td style={tdStyle}>
                  <Badge label={stage.label} color={stage.color} background={stage.background} />
                </td>
                <td style={tdStyle}>
                  <span style={{ fontSize: 13, color: '#475467', fontWeight: 650 }}>{issue.severity}</span>
                </td>
                <td style={tdStyle}>
                  <Badge label={status.label} color={status.color} background={status.background} />
                </td>
                <td style={tdStyle}>
                  <Badge
                    label={fixSourceLabel}
                    color={fixSource?.color ?? '#475467'}
                    background={fixSource?.background ?? '#f2f4f7'}
                  />
                </td>
                <td style={tdStyle}>
                  <Badge label={category.label} color={category.color} background={category.background} />
                </td>
                <td style={tdStyle}>
                  <span style={{ fontSize: 13, color: '#475467', fontWeight: 650 }}>{issue.found_in_count}</span>
                </td>
                <td style={tdStyle}>
                  {issue.pr_numbers.length > 0 ? (
                    <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
                      {issue.pr_numbers.map((pr) => (
                        <a
                          key={pr}
                          href={`https://github.com/tidbcloud/investibook/pull/${pr}`}
                          target="_blank"
                          rel="noreferrer"
                          style={{ color: '#175cd3', fontSize: 13, fontWeight: 740, textDecoration: 'none' }}
                        >
                          #{pr}
                        </a>
                      ))}
                    </div>
                  ) : (
                    <span style={{ color: '#c0c5d0' }}>-</span>
                  )}
                </td>
              </tr>
            );
          })}
        </tbody>
      </table>
    </div>
  );
}

const thStyle: CSSProperties = {
  padding: '11px 14px',
  textAlign: 'left',
  color: '#667085',
  fontSize: 12,
  fontWeight: 760,
  whiteSpace: 'nowrap',
};

const tdStyle: CSSProperties = {
  padding: '13px 14px',
  verticalAlign: 'top',
};

export default function TriagePilotPage() {
  const router = useRouter();
  const { t } = useTranslation();
  const [data, setData] = useState<TriageResponse | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [query, setQuery] = useState('');
  const [statusFilter, setStatusFilter] = useState<(typeof STATUS_OPTIONS)[number]>('all');
  const [severityFilter, setSeverityFilter] = useState('all');
  const [stageFilter, setStageFilter] = useState('all');
  const [trackFilter, setTrackFilter] = useState('all');
  const [fixSourceFilter, setFixSourceFilter] = useState('all');
  const [categoryFilter, setCategoryFilter] = useState('all');

  const fixSourceLabels = useMemo(
    () => Object.fromEntries(
      Object.entries(FIX_SOURCE_META).map(([source, meta]) => [source, t(meta.labelKey)]),
    ),
    [t],
  );

  useEffect(() => {
    const load = async () => {
      try {
        const apiUrl = process.env.NEXT_PUBLIC_API_URL || '';
        const response = await fetchWithAuth(`${apiUrl}/api/admin/triagepilot/issues`);
        if (!response.ok) {
          if (response.status === 401) {
            router.push('/login');
            return;
          }
          throw new Error('Failed to load TriagePilot issues');
        }
        setData(await response.json());
      } catch (err) {
        setError(err instanceof Error ? err.message : 'Unknown error');
      } finally {
        setLoading(false);
      }
    };
    load();
  }, [router]);

  const severityOptions = useMemo(() => {
    if (!data) return ['all'];
    const rank = (severity: string) => {
      const match = severity.match(/^P(\d+)/);
      return match ? Number(match[1]) : 99;
    };
    return ['all', ...Object.keys(data.summary.by_severity).sort((a, b) => rank(a) - rank(b) || a.localeCompare(b))];
  }, [data]);

  const trackOptions = useMemo(() => {
    if (!data) return ['all'];
    const rank = (track: string) => (track === 'skill_quality' ? 0 : track === 'data_tooling' ? 1 : 99);
    return ['all', ...Object.keys(data.summary.by_diagnosis_track).sort((a, b) => rank(a) - rank(b) || a.localeCompare(b))];
  }, [data]);

  const categoryOptions = useMemo(() => {
    if (!data) return ['all'];
    return ['all', ...Object.keys(data.summary.by_autofix_category).sort()];
  }, [data]);

  const fixSourceOptions = useMemo(() => {
    if (!data) return ['all'];
    const rank = (source: string) => (source === 'autofix' ? 0 : source === 'manual' ? 1 : 99);
    return ['all', ...Object.keys(data.summary.by_fix_source)
      .filter((source) => source !== 'unfixed')
      .sort((a, b) => rank(a) - rank(b) || a.localeCompare(b))];
  }, [data]);

  const handledSourceCounts = useMemo(() => {
    if (!data) return {};
    return Object.fromEntries(Object.entries(data.summary.by_fix_source).filter(([source]) => source !== 'unfixed'));
  }, [data]);

  const stageCounts = useMemo(() => {
    if (!data) return {};
    const rank = (stage: string) => {
      const index = STAGE_ORDER.indexOf(stage);
      return index === -1 ? 99 : index;
    };
    return Object.fromEntries(
      Object.entries(data.summary.by_issue_stage).sort((a, b) => rank(a[0]) - rank(b[0]) || b[1] - a[1]),
    );
  }, [data]);

  const filteredIssues = useMemo(() => {
    if (!data) return [];
    const normalizedQuery = query.trim().toLowerCase();
    return data.issues.filter((issue) => {
      if (statusFilter !== 'all' && issue.status !== statusFilter) return false;
      if (severityFilter !== 'all' && issue.severity !== severityFilter) return false;
      if (stageFilter !== 'all' && issue.issue_stage !== stageFilter) return false;
      if (trackFilter !== 'all' && issue.diagnosis_track !== trackFilter) return false;
      if (fixSourceFilter !== 'all' && issue.fix_source !== fixSourceFilter) return false;
      if (categoryFilter !== 'all' && issue.autofix_category !== categoryFilter) return false;
      if (!normalizedQuery) return true;
      return [issue.id, issue.title, issue.type, issue.issue_stage, issue.severity, issue.diagnosis_track, issue.fix_source, issue.fixed_in, issue.github_issue]
        .join(' ')
        .toLowerCase()
        .includes(normalizedQuery);
    });
  }, [categoryFilter, data, fixSourceFilter, query, severityFilter, stageFilter, statusFilter, trackFilter]);

  const clearFilters = () => {
    setQuery('');
    setStatusFilter('all');
    setSeverityFilter('all');
    setStageFilter('all');
    setTrackFilter('all');
    setFixSourceFilter('all');
    setCategoryFilter('all');
  };

  const applyStatusFilter = (status: (typeof STATUS_OPTIONS)[number]) => {
    setQuery('');
    setStatusFilter(status);
    setSeverityFilter('all');
    setStageFilter('all');
    setTrackFilter('all');
    setFixSourceFilter('all');
    setCategoryFilter('all');
  };

  const toggleStageFilter = (stage: string) => {
    setQuery('');
    setStageFilter((current) => current === stage ? 'all' : stage);
  };

  const toggleCategoryFilter = (category: string) => {
    setQuery('');
    setCategoryFilter((current) => current === category ? 'all' : category);
  };

  const toggleFixSourceFilter = (source: string) => {
    setQuery('');
    setFixSourceFilter((current) => current === source ? 'all' : source);
  };

  const activeFilterLabels = [
    query.trim() ? `Search: ${query.trim()}` : '',
    statusFilter !== 'all' ? `Status: ${STATUS_META[statusFilter]?.label ?? statusFilter}` : '',
    severityFilter !== 'all' ? `P-level: ${severityFilter}` : '',
    stageFilter !== 'all' ? `Stage: ${STAGE_META[stageFilter]?.label ?? stageFilter}` : '',
    trackFilter !== 'all' ? `Track: ${TRACK_META[trackFilter]?.label ?? trackFilter}` : '',
    fixSourceFilter !== 'all' ? `${t('adminTriagePilot.fixSource')}: ${fixSourceLabels[fixSourceFilter] ?? fixSourceFilter}` : '',
    categoryFilter !== 'all' ? `Autofix: ${CATEGORY_META[categoryFilter]?.label ?? categoryFilter}` : '',
  ].filter(Boolean);

  const noFilters = activeFilterLabels.length === 0;

  if (loading) {
    return (
      <div style={{ paddingTop: 40, textAlign: 'center', color: '#9ca3af', fontSize: 14 }}>
        Loading TriagePilot board…
      </div>
    );
  }

  if (error || !data) {
    return (
      <div style={{ paddingTop: 40, textAlign: 'center', color: '#b42318', fontSize: 14 }}>
        {error || 'No TriagePilot data'}
      </div>
    );
  }

  const summary = data.summary;
  const autofixHandled = summary.by_fix_source.autofix ?? 0;
  const generatedAt = new Date(data.generated_at).toLocaleString();

  return (
    <div style={{ paddingTop: 8, paddingBottom: 32 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', gap: 16, alignItems: 'flex-start', marginBottom: 24 }}>
        <div>
          <h1 style={{ margin: 0, fontSize: 22, fontWeight: 780, color: '#101828' }}>
            TriagePilot Board
          </h1>
          <p style={{ margin: '7px 0 0', fontSize: 14, color: '#667085', lineHeight: 1.5 }}>
            ISS backlog, autofix candidates, and shipped fixes from the replayer registry
          </p>
        </div>
        <div style={{ textAlign: 'right', color: '#98a2b3', fontSize: 12, lineHeight: 1.5, minWidth: 160 }}>
          <div>{generatedAt}</div>
          <div>{data.parse_mode} parser</div>
        </div>
      </div>

      <div
        style={{
          display: 'grid',
          gridTemplateColumns: 'repeat(auto-fit, minmax(170px, 1fr))',
          gap: 14,
          marginBottom: 18,
        }}
      >
        <StatCard
          label="Total ISS"
          value={summary.total}
          tone="#344054"
          active={noFilters}
          onClick={clearFilters}
        />
        <StatCard
          label="Open backlog"
          value={summary.open}
          tone="#b42318"
          active={statusFilter === 'open' && severityFilter === 'all' && stageFilter === 'all' && trackFilter === 'all' && fixSourceFilter === 'all' && categoryFilter === 'all'}
          onClick={() => applyStatusFilter('open')}
        />
        <StatCard
          label="In progress"
          value={summary.in_progress}
          tone="#b54708"
          active={statusFilter === 'in_progress' && severityFilter === 'all' && stageFilter === 'all' && trackFilter === 'all' && fixSourceFilter === 'all' && categoryFilter === 'all'}
          onClick={() => applyStatusFilter('in_progress')}
        />
        <StatCard
          label="Fixed"
          value={summary.fixed}
          tone="#067647"
          active={statusFilter === 'fixed' && severityFilter === 'all' && stageFilter === 'all' && trackFilter === 'all' && fixSourceFilter === 'all' && categoryFilter === 'all'}
          onClick={() => applyStatusFilter('fixed')}
        />
        <StatCard
          label={t('adminTriagePilot.autofixHandled')}
          value={autofixHandled}
          tone="#175cd3"
          active={statusFilter === 'all' && severityFilter === 'all' && stageFilter === 'all' && trackFilter === 'all' && fixSourceFilter === 'autofix' && categoryFilter === 'all'}
          onClick={() => {
            setQuery('');
            setStatusFilter('all');
            setSeverityFilter('all');
            setStageFilter('all');
            setTrackFilter('all');
            setFixSourceFilter('autofix');
            setCategoryFilter('all');
          }}
        />
        <StatCard
          label="Autofixable"
          value={summary.by_autofix_category.autofixable ?? 0}
          tone="#4548a3"
          suffix="open"
          active={categoryFilter === 'autofixable' && statusFilter === 'all' && severityFilter === 'all' && stageFilter === 'all' && trackFilter === 'all' && fixSourceFilter === 'all'}
          onClick={() => {
            setQuery('');
            setStatusFilter('all');
            setSeverityFilter('all');
            setStageFilter('all');
            setTrackFilter('all');
            setFixSourceFilter('all');
            setCategoryFilter('autofixable');
          }}
        />
      </div>

      <TriageTrendChart trend={data.trend} />

      <div
        style={{
          display: 'grid',
          gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))',
          gap: 16,
          marginBottom: 18,
        }}
      >
        <div style={{ background: '#fff', border: '1px solid #e5e7eb', borderRadius: 8, padding: 16 }}>
          <div style={{ fontSize: 13, fontWeight: 780, color: '#101828', marginBottom: 14 }}>Stage distribution</div>
          <DistributionBars
            counts={stageCounts}
            activeValue={stageFilter}
            onSelect={toggleStageFilter}
            meta={STAGE_META}
            order={STAGE_ORDER}
          />
        </div>
        <div style={{ background: '#fff', border: '1px solid #e5e7eb', borderRadius: 8, padding: 16 }}>
          <div style={{ fontSize: 13, fontWeight: 780, color: '#101828', marginBottom: 14 }}>
            {t('adminTriagePilot.fixSource')}
          </div>
          <DistributionBars
            counts={handledSourceCounts}
            activeValue={fixSourceFilter}
            onSelect={toggleFixSourceFilter}
            labelOverrides={fixSourceLabels}
          />
        </div>
        <div style={{ background: '#fff', border: '1px solid #e5e7eb', borderRadius: 8, padding: 16 }}>
          <div style={{ fontSize: 13, fontWeight: 780, color: '#101828', marginBottom: 14 }}>
            {t('adminTriagePilot.autofixState')}
          </div>
          <DistributionBars
            counts={summary.by_autofix_category}
            activeValue={categoryFilter}
            onSelect={toggleCategoryFilter}
          />
        </div>
      </div>

      <div
        style={{
          background: '#fff',
          border: '1px solid #e5e7eb',
          borderRadius: 8,
          padding: 14,
          marginBottom: 18,
          display: 'flex',
          flexWrap: 'wrap',
          gap: 10,
          alignItems: 'center',
        }}
      >
        <input
          value={query}
          onChange={(event) => setQuery(event.target.value)}
          placeholder="Search issue, PR, title"
          style={{
            height: 36,
            minWidth: 220,
            flex: '1 1 240px',
            border: '1px solid #d0d5dd',
            borderRadius: 7,
            padding: '0 11px',
            fontSize: 14,
            outline: 'none',
          }}
        />
        <select
          value={statusFilter}
          onChange={(event) => setStatusFilter(event.target.value as (typeof STATUS_OPTIONS)[number])}
          style={selectStyle}
        >
          {STATUS_OPTIONS.map((status) => (
            <option key={status} value={status}>
              {status === 'all' ? 'All status' : STATUS_META[status]?.label ?? status}
            </option>
          ))}
        </select>
        <select
          value={severityFilter}
          onChange={(event) => setSeverityFilter(event.target.value)}
          style={selectStyle}
        >
          {severityOptions.map((severity) => (
            <option key={severity} value={severity}>{severity === 'all' ? 'All P-levels' : severity}</option>
          ))}
        </select>
        <select
          value={trackFilter}
          onChange={(event) => setTrackFilter(event.target.value)}
          style={selectStyle}
        >
          {trackOptions.map((track) => (
            <option key={track} value={track}>{track === 'all' ? 'All tracks' : TRACK_META[track]?.label ?? track}</option>
          ))}
        </select>
        <select
          value={fixSourceFilter}
          onChange={(event) => setFixSourceFilter(event.target.value)}
          style={selectStyle}
        >
          {fixSourceOptions.map((source) => (
            <option key={source} value={source}>{source === 'all' ? t('adminTriagePilot.allFixSources') : fixSourceLabels[source] ?? source}</option>
          ))}
        </select>
        <select
          value={categoryFilter}
          onChange={(event) => setCategoryFilter(event.target.value)}
          style={selectStyle}
        >
          {categoryOptions.map((category) => (
            <option key={category} value={category}>
              {category === 'all' ? 'All autofix states' : CATEGORY_META[category]?.label ?? category}
            </option>
          ))}
        </select>
        <button
          onClick={clearFilters}
          disabled={noFilters}
          style={{
            height: 36,
            border: '1px solid #d0d5dd',
            borderRadius: 7,
            padding: '0 11px',
            background: noFilters ? '#f8fafc' : '#fff',
            color: noFilters ? '#c0c5d0' : '#344054',
            cursor: noFilters ? 'default' : 'pointer',
            fontSize: 13,
            fontWeight: 700,
          }}
        >
          Clear
        </button>
        <div style={{ flexBasis: '100%', display: 'flex', gap: 8, flexWrap: 'wrap', alignItems: 'center' }}>
          <span style={{ color: '#98a2b3', fontSize: 12, fontWeight: 700 }}>
            Showing {filteredIssues.length} of {summary.total}
          </span>
          {activeFilterLabels.map((label) => (
            <span
              key={label}
              style={{
                background: '#f4f3ff',
                color: '#5c5fbc',
                borderRadius: 6,
                padding: '3px 8px',
                fontSize: 12,
                fontWeight: 700,
              }}
            >
              {label}
            </span>
          ))}
        </div>
      </div>

      <section style={{ background: '#fff', border: '1px solid #e5e7eb', borderRadius: 8, marginBottom: 18, overflow: 'hidden' }}>
        <div style={sectionHeaderStyle}>
          <div>
            <h2 style={sectionTitleStyle}>Issues</h2>
            <div style={sectionSubStyle}>{filteredIssues.length} issues match the current filters</div>
          </div>
        </div>
        <IssueTable
          issues={filteredIssues}
          emptyLabel="No issues match the current filters."
          fixSourceLabels={fixSourceLabels}
          fixSourceHeader={t('adminTriagePilot.fixSource')}
          autofixStateHeader={t('adminTriagePilot.autofixState')}
        />
      </section>
    </div>
  );
}

const selectStyle: CSSProperties = {
  height: 36,
  border: '1px solid #d0d5dd',
  borderRadius: 7,
  padding: '0 9px',
  fontSize: 13,
  fontWeight: 650,
  color: '#344054',
  background: '#fff',
  minWidth: 142,
};

const sectionHeaderStyle: CSSProperties = {
  padding: '15px 16px',
  borderBottom: '1px solid #e5e7eb',
  background: '#fcfcfd',
};

const sectionTitleStyle: CSSProperties = {
  margin: 0,
  color: '#101828',
  fontSize: 15,
  fontWeight: 780,
};

const sectionSubStyle: CSSProperties = {
  marginTop: 4,
  color: '#667085',
  fontSize: 13,
};
