'use client'

import React, { useState, useEffect, useRef, useMemo } from 'react'
import { trpc } from '@/lib/trpc/client'
import { toast } from 'sonner'
import { cn } from '@/lib/utils'
import {
  DndContext,
  closestCenter,
  KeyboardSensor,
  PointerSensor,
  useSensor,
  useSensors,
  type DragEndEvent,
} from '@dnd-kit/core'
import {
  arrayMove,
  SortableContext,
  sortableKeyboardCoordinates,
  useSortable,
  verticalListSortingStrategy,
} from '@dnd-kit/sortable'
import { CSS } from '@dnd-kit/utilities'
import { AnimatePresence, motion } from 'motion/react'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'
import { Skeleton } from '@/components/ui/skeleton'
import {
  Sheet,
  SheetContent,
  SheetHeader,
  SheetTitle,
  SheetDescription,
} from '@/components/ui/sheet'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Textarea } from '@/components/ui/textarea'
import { Slider } from '@/components/ui/slider'
import {
  Collapsible,
  CollapsibleContent,
  CollapsibleTrigger,
} from '@/components/ui/collapsible'
import {
  GripVertical,
  BarChart3,
  Calculator,
  Loader2,
  RefreshCw,
  Sparkles,
  ExternalLink,
  ChevronDown,
  Settings2,
  Download,
} from 'lucide-react'
import type { RankedProjectEntry } from '@/server/services/ai-ranking'

// ─── Types ────────────────────────────────────────────────────────────────────

type RankingDashboardProps = {
  competitionId: string
  roundId: string
}

type ProjectInfo = {
  title: string
  teamName: string | null
  country: string | null
}

type JurorScore = {
  jurorName: string
  globalScore: number | null
  decision: boolean | null
}

type SortableProjectRowProps = {
  projectId: string
  currentRank: number
  entry: (RankedProjectEntry & { originalIndex?: number }) | undefined
  projectInfo: ProjectInfo | undefined
  jurorScores: JurorScore[] | undefined
  onSelect: () => void
  isSelected: boolean
  originalRank: number | undefined // from snapshotOrder — always in sync with localOrder
}

// ─── Sub-component: SortableProjectRow ────────────────────────────────────────

function SortableProjectRow({
  projectId,
  currentRank,
  entry,
  projectInfo,
  jurorScores,
  onSelect,
  isSelected,
  originalRank,
}: SortableProjectRowProps) {
  const {
    attributes,
    listeners,
    setNodeRef,
    transform,
    transition,
    isDragging,
  } = useSortable({ id: projectId })

  const style = {
    transform: CSS.Transform.toString(transform),
    transition,
  }

  // isOverridden: admin drag-reordered this project from its original snapshot position.
  // Uses snapshotOrder (set in same effect as localOrder) so they are always in sync.
  const isOverridden = originalRank !== undefined && currentRank !== originalRank

  // Compute yes count from juror scores
  const yesCount = jurorScores?.filter((j) => j.decision === true).length ?? 0
  const totalJurors = jurorScores?.length ?? entry?.evaluatorCount ?? 0

  return (
    <div
      ref={setNodeRef}
      style={style}
      onClick={onSelect}
      className={cn(
        'flex items-center gap-3 rounded-lg border bg-card p-3 cursor-pointer transition-all hover:shadow-sm',
        isDragging && 'opacity-50 shadow-lg ring-2 ring-[#de0f1e]/30',
        isSelected && 'ring-2 ring-[#de0f1e]',
      )}
    >
      {/* Drag handle */}
      <button
        className="cursor-grab touch-none text-muted-foreground hover:text-foreground flex-shrink-0"
        onClick={(e) => e.stopPropagation()}
        {...attributes}
        {...listeners}
      >
        <GripVertical className="h-4 w-4" />
      </button>

      {/* Rank badge */}
      {isOverridden ? (
        <Badge className="flex-shrink-0 bg-amber-100 text-amber-700 hover:bg-amber-100 border-amber-200 text-xs font-semibold">
          #{currentRank} (override)
        </Badge>
      ) : (
        <Badge
          className="flex-shrink-0 text-xs font-semibold"
          style={{ backgroundColor: '#053d57', color: '#fefefe' }}
        >
          #{currentRank}
        </Badge>
      )}

      {/* Project info */}
      <div className="flex-1 min-w-0">
        <p className="text-sm font-medium truncate">
          {projectInfo?.title ?? `Project …${projectId.slice(-6)}`}
        </p>
        {projectInfo?.teamName && (
          <p className="text-xs text-muted-foreground truncate">
            {projectInfo.teamName}
            {projectInfo.country ? ` · ${projectInfo.country}` : ''}
          </p>
        )}
      </div>

      {/* Juror scores + advance decision */}
      <div className="flex items-center gap-3 flex-shrink-0">
        {/* Individual juror score pills */}
        {jurorScores && jurorScores.length > 0 ? (
          <div className="flex items-center gap-1" title={jurorScores.map((j) => `${j.jurorName}: ${j.globalScore ?? '—'}/10`).join('\n')}>
            {jurorScores.map((j, i) => (
              <span
                key={i}
                className={cn(
                  'inline-flex items-center justify-center rounded-md px-1.5 py-0.5 text-xs font-medium border',
                  j.globalScore != null && j.globalScore >= 8
                    ? 'bg-emerald-50 text-emerald-700 border-emerald-200'
                    : j.globalScore != null && j.globalScore >= 6
                      ? 'bg-blue-50 text-blue-700 border-blue-200'
                      : j.globalScore != null && j.globalScore >= 4
                        ? 'bg-amber-50 text-amber-700 border-amber-200'
                        : 'bg-red-50 text-red-700 border-red-200',
                )}
                title={`${j.jurorName}: ${j.globalScore ?? '—'}/10`}
              >
                {j.globalScore ?? '—'}
              </span>
            ))}
          </div>
        ) : entry?.avgGlobalScore !== null && entry?.avgGlobalScore !== undefined ? (
          <span className="text-xs text-muted-foreground">
            Avg {entry.avgGlobalScore.toFixed(1)}
          </span>
        ) : null}

        {/* Average score */}
        {entry?.avgGlobalScore !== null && entry?.avgGlobalScore !== undefined && jurorScores && jurorScores.length > 1 && (
          <span className="text-xs font-medium text-muted-foreground" title="Average score">
            = {entry.avgGlobalScore.toFixed(1)}
          </span>
        )}

        {/* Advance decision indicator */}
        <div className={cn(
          'inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium',
          totalJurors === 0
            ? 'bg-gray-100 text-gray-500'
            : yesCount === totalJurors
              ? 'bg-emerald-100 text-emerald-700'
              : yesCount > 0
                ? 'bg-amber-100 text-amber-700'
                : 'bg-red-100 text-red-600',
        )}>
          {totalJurors > 0 ? (
            <>{yesCount}/{totalJurors} Yes</>
          ) : (
            <>0 jurors</>
          )}
        </div>
      </div>
    </div>
  )
}

// ─── Main component ────────────────────────────────────────────────────────────

export function RankingDashboard({ competitionId: _competitionId, roundId }: RankingDashboardProps) {
  // ─── State ────────────────────────────────────────────────────────────────
  const [selectedProjectId, setSelectedProjectId] = useState<string | null>(null)
  const [localOrder, setLocalOrder] = useState<Record<'STARTUP' | 'BUSINESS_CONCEPT', string[]>>({
    STARTUP: [],
    BUSINESS_CONCEPT: [],
  })
  // Track the original snapshot order (projectId → 1-based rank) for override detection.
  // Updated in the same effect as localOrder so they are always in sync.
  const [snapshotOrder, setSnapshotOrder] = useState<Record<string, number>>({})
  const initialized = useRef(false)
  const pendingReorderCount = useRef(0)

  // ─── Export state ──────────────────────────────────────────────────────────
  const [exportLoading, setExportLoading] = useState(false)

  // ─── Expandable review state ──────────────────────────────────────────────
  const [expandedReviews, setExpandedReviews] = useState<Set<string>>(new Set())

  // ─── Criteria weights state ────────────────────────────────────────────────
  const [weightsOpen, setWeightsOpen] = useState(false)
  const [localWeights, setLocalWeights] = useState<Record<string, number>>({})
  const [localCriteriaText, setLocalCriteriaText] = useState<string>('')
  const [localScoreWeight, setLocalScoreWeight] = useState(5)
  const [localPassRateWeight, setLocalPassRateWeight] = useState(5)
  const weightsInitialized = useRef(false)

  // ─── Sensors ──────────────────────────────────────────────────────────────
  const sensors = useSensors(
    useSensor(PointerSensor),
    useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }),
  )

  // ─── tRPC queries ─────────────────────────────────────────────────────────
  const { data: snapshots, isLoading: snapshotsLoading } = trpc.ranking.listSnapshots.useQuery(
    { roundId },
    // Poll every 3s so all admins see ranking progress/completion quickly
    { refetchInterval: 3_000 },
  )

  // Derive ranking-in-progress from server state (visible to ALL admins)
  const rankingInProgress = snapshots?.[0]?.status === 'RUNNING'

  // Find the latest COMPLETED snapshot (skip RUNNING/FAILED)
  const latestCompleted = snapshots?.find((s) => s.status === 'COMPLETED')
  const latestSnapshotId = latestCompleted?.id ?? null
  const latestSnapshot = latestCompleted ?? null

  const { data: snapshot, isLoading: snapshotLoading } = trpc.ranking.getSnapshot.useQuery(
    { snapshotId: latestSnapshotId! },
    { enabled: !!latestSnapshotId },
  )

  const { data: projectStates } = trpc.roundEngine.getProjectStates.useQuery(
    { roundId },
  )

  const { data: projectDetail, isLoading: detailLoading } = trpc.project.getFullDetail.useQuery(
    { id: selectedProjectId! },
    { enabled: !!selectedProjectId },
  )

  const { data: roundData } = trpc.round.getById.useQuery({ id: roundId })

  const { data: evalScores } = trpc.ranking.roundEvaluationScores.useQuery(
    { roundId },
  )

  const { data: evalForm } = trpc.evaluation.getStageForm.useQuery(
    { roundId },
  )

  // ─── tRPC mutations ───────────────────────────────────────────────────────
  const utils = trpc.useUtils()

  const saveReorderMutation = trpc.ranking.saveReorder.useMutation({
    onMutate: () => { pendingReorderCount.current++ },
    onSettled: () => { pendingReorderCount.current-- },
    onError: (err) => toast.error(`Failed to save order: ${err.message}`),
    // Do NOT invalidate getSnapshot — would reset localOrder
  })

  const updateRoundMutation = trpc.round.update.useMutation({
    onSuccess: () => {
      toast.success('Ranking config saved')
      void utils.round.getById.invalidate({ id: roundId })
    },
    onError: (err) => toast.error(`Failed to save: ${err.message}`),
  })

  const triggerRankMutation = trpc.ranking.triggerAutoRank.useMutation({
    onSuccess: () => {
      toast.success('Ranking complete!')
      initialized.current = false // allow re-init on next snapshot load
      void utils.ranking.listSnapshots.invalidate({ roundId })
      void utils.ranking.getSnapshot.invalidate()
    },
    onError: (err) => {
      toast.error(err.message)
      void utils.ranking.listSnapshots.invalidate({ roundId })
    },
  })

  // ─── evalConfig (advancement counts from round config) ────────────────────
  const evalConfig = useMemo(() => {
    if (!roundData?.configJson) return null
    try {
      const config = roundData.configJson as Record<string, unknown>
      const advConfig = config.advancementConfig as Record<string, unknown> | undefined
      return {
        advanceMode: (config.advanceMode as string) ?? 'count',
        advanceScoreThreshold: (config.advanceScoreThreshold as number) ?? undefined,
        startupAdvanceCount: (advConfig?.startupCount ?? config.startupAdvanceCount ?? 0) as number,
        conceptAdvanceCount: (advConfig?.conceptCount ?? config.conceptAdvanceCount ?? 0) as number,
      }
    } catch { return null }
  }, [roundData])

  // ─── rankingMap (O(1) lookup) ──────────────────────────────────────────────
  const rankingMap = useMemo(() => {
    const map = new Map<string, RankedProjectEntry & { originalIndex: number }>()
    if (!snapshot) return map
    const startup = (snapshot.startupRankingJson ?? []) as unknown as RankedProjectEntry[]
    const concept = (snapshot.conceptRankingJson ?? []) as unknown as RankedProjectEntry[]
    startup.forEach((entry, i) => map.set(entry.projectId, { ...entry, originalIndex: i + 1 }))
    concept.forEach((entry, i) => map.set(entry.projectId, { ...entry, originalIndex: i + 1 }))
    return map
  }, [snapshot])

  // ─── projectInfoMap (O(1) lookup by projectId) ────────────────────────────
  const projectInfoMap = useMemo(() => {
    const map = new Map<string, ProjectInfo>()
    if (!projectStates) return map
    for (const ps of projectStates) {
      map.set(ps.project.id, {
        title: ps.project.title,
        teamName: ps.project.teamName,
        country: ps.project.country,
      })
    }
    return map
  }, [projectStates])

  // ─── localOrder init (once, with useRef guard) ────────────────────────────
  useEffect(() => {
    if (!initialized.current && snapshot) {
      const startup = (snapshot.startupRankingJson ?? []) as unknown as RankedProjectEntry[]
      const concept = (snapshot.conceptRankingJson ?? []) as unknown as RankedProjectEntry[]

      // Deduplicate ranking entries (AI may return duplicates) — keep first occurrence
      const dedup = (arr: RankedProjectEntry[]): RankedProjectEntry[] => {
        const seen = new Set<string>()
        return arr.filter((r) => {
          if (seen.has(r.projectId)) return false
          seen.add(r.projectId)
          return true
        })
      }
      const dedupedStartup = dedup(startup)
      const dedupedConcept = dedup(concept)

      // Sort by avgGlobalScore descending (the metric displayed to the admin), with
      // compositeScore as tiebreaker.  This ensures the visible ordering matches the
      // numbers on screen AND the threshold cutoff line lands correctly (it checks
      // avgGlobalScore, so the list must be sorted by that same metric).
      dedupedStartup.sort((a, b) =>
        (b.avgGlobalScore ?? 0) - (a.avgGlobalScore ?? 0) || b.compositeScore - a.compositeScore)
      dedupedConcept.sort((a, b) =>
        (b.avgGlobalScore ?? 0) - (a.avgGlobalScore ?? 0) || b.compositeScore - a.compositeScore)

      // Track original order for override detection (same effect = always in sync)
      const order: Record<string, number> = {}
      dedupedStartup.forEach((r, i) => { order[r.projectId] = i + 1 })
      dedupedConcept.forEach((r, i) => { order[r.projectId] = i + 1 })
      setSnapshotOrder(order)

      // Apply saved reorders so the ranking persists across all admin sessions.
      // reordersJson is append-only — the latest event per category is the current order.
      const reorders = (snapshot.reordersJson as Array<{
        category: 'STARTUP' | 'BUSINESS_CONCEPT'
        orderedProjectIds: string[]
      }> | null) ?? []

      const latestStartupReorder = [...reorders].reverse().find((r) => r.category === 'STARTUP')
      const latestConceptReorder = [...reorders].reverse().find((r) => r.category === 'BUSINESS_CONCEPT')

      // Deduplicate reorder IDs too, and filter out IDs not in the current snapshot
      const dedupIds = (ids: string[], validSet: Set<string>): string[] => {
        const seen = new Set<string>()
        return ids.filter((id) => {
          if (seen.has(id) || !validSet.has(id)) return false
          seen.add(id)
          return true
        })
      }
      const startupIdSet = new Set(dedupedStartup.map((r) => r.projectId))
      const conceptIdSet = new Set(dedupedConcept.map((r) => r.projectId))

      setLocalOrder({
        STARTUP: latestStartupReorder
          ? dedupIds(latestStartupReorder.orderedProjectIds, startupIdSet)
          : dedupedStartup.map((r) => r.projectId),
        BUSINESS_CONCEPT: latestConceptReorder
          ? dedupIds(latestConceptReorder.orderedProjectIds, conceptIdSet)
          : dedupedConcept.map((r) => r.projectId),
      })

      initialized.current = true
    }
  }, [snapshot])

  // ─── numericCriteria from eval form ─────────────────────────────────────
  const numericCriteria = useMemo(() => {
    if (!evalForm?.criteriaJson) return []
    return (evalForm.criteriaJson as Array<{ id: string; label: string; type?: string; scale?: number | string }>)
      .filter((c) => !c.type || c.type === 'numeric')
  }, [evalForm])

  // ─── Init local weights + criteriaText from round config ──────────────────
  useEffect(() => {
    if (!weightsInitialized.current && roundData?.configJson) {
      const cfg = roundData.configJson as Record<string, unknown>
      const saved = (cfg.criteriaWeights ?? {}) as Record<string, number>
      setLocalWeights(saved)
      setLocalCriteriaText((cfg.rankingCriteria as string) ?? '')
      setLocalScoreWeight((cfg.scoreWeight as number) ?? 5)
      setLocalPassRateWeight((cfg.passRateWeight as number) ?? 5)
      weightsInitialized.current = true
    }
  }, [roundData])

  // ─── Save weights + criteria text to round config ─────────────────────────
  const saveRankingConfig = () => {
    if (!roundData?.configJson) return
    const cfg = roundData.configJson as Record<string, unknown>
    updateRoundMutation.mutate({
      id: roundId,
      configJson: {
        ...cfg,
        criteriaWeights: localWeights,
        rankingCriteria: localCriteriaText,
        scoreWeight: localScoreWeight,
        passRateWeight: localPassRateWeight,
      },
    })
  }

  // Derive ranking mode from criteria text
  const isFormulaMode = !localCriteriaText.trim()

  // ─── handleDragEnd ────────────────────────────────────────────────────────
  function handleDragEnd(category: 'STARTUP' | 'BUSINESS_CONCEPT', event: DragEndEvent) {
    const { active, over } = event
    if (!over || active.id === over.id) return
    setLocalOrder((prev) => {
      const ids = prev[category]
      const newIds = arrayMove(
        ids,
        ids.indexOf(active.id as string),
        ids.indexOf(over.id as string),
      )
      saveReorderMutation.mutate({
        snapshotId: latestSnapshotId!,
        category,
        orderedProjectIds: newIds,
      })
      return { ...prev, [category]: newIds }
    })
  }

  // ─── handleExport ──────────────────────────────────────────────────────────
  async function handleExportScores() {
    setExportLoading(true)
    try {
      const result = await utils.export.projectScores.fetch({ roundId })
      if (!result.data || result.data.length === 0) {
        toast.error('No data to export')
        return
      }
      const headers = result.columns
      const csvRows = [
        headers.join(','),
        ...result.data.map((row: Record<string, unknown>) =>
          headers.map((h: string) => {
            const val = row[h]
            if (val == null) return ''
            const str = String(val)
            return str.includes(',') || str.includes('"') || str.includes('\n')
              ? `"${str.replace(/"/g, '""')}"`
              : str
          }).join(','),
        ),
      ]
      const blob = new Blob([csvRows.join('\n')], { type: 'text/csv;charset=utf-8;' })
      const url = URL.createObjectURL(blob)
      const a = document.createElement('a')
      a.href = url
      a.download = `round-scores-${roundId.slice(-8)}.csv`
      a.click()
      URL.revokeObjectURL(url)
      toast.success('CSV exported')
    } catch (err) {
      toast.error(`Export failed: ${err instanceof Error ? err.message : 'Unknown error'}`)
    } finally {
      setExportLoading(false)
    }
  }

  // ─── Loading state ────────────────────────────────────────────────────────
  if (snapshotsLoading || snapshotLoading) {
    return (
      <div className="space-y-4">
        <Skeleton className="h-24 w-full rounded-lg" />
        <Skeleton className="h-48 w-full rounded-lg" />
        <Skeleton className="h-48 w-full rounded-lg" />
      </div>
    )
  }

  // ─── Empty state ──────────────────────────────────────────────────────────
  if (!latestSnapshotId) {
    return (
      <div className="space-y-4">
        <Card>
          <CardContent className="flex flex-col items-center justify-center gap-4 py-12 text-center">
            {rankingInProgress ? (
              <>
                <Loader2 className="h-10 w-10 text-blue-500 animate-spin" />
                <div>
                  <p className="font-medium">Ranking in progress&hellip;</p>
                  <p className="mt-1 text-sm text-muted-foreground">
                    This may take a minute. You can continue working — results will appear automatically.
                  </p>
                </div>
                <div className="h-2 w-48 rounded-full bg-blue-100 dark:bg-blue-900 overflow-hidden">
                  <div className="h-full w-full rounded-full bg-blue-500 animate-pulse" />
                </div>
              </>
            ) : (
              <>
                <BarChart3 className="h-10 w-10 text-muted-foreground" />
                <div>
                  <p className="font-medium">No ranking available yet</p>
                  <p className="mt-1 text-sm text-muted-foreground">
                    Run ranking from the Config tab to generate results, or trigger it now.
                  </p>
                </div>
                <Button
                  onClick={() => triggerRankMutation.mutate({ roundId })}
                  disabled={triggerRankMutation.isPending}
                >
                  {isFormulaMode ? (
                    <Calculator className="mr-2 h-4 w-4" />
                  ) : (
                    <Sparkles className="mr-2 h-4 w-4" />
                  )}
                  {isFormulaMode ? 'Run Ranking Now' : 'Run AI Ranking Now'}
                </Button>
              </>
            )}
          </CardContent>
        </Card>
      </div>
    )
  }

  // ─── Main content ─────────────────────────────────────────────────────────
  const categoryLabels: Record<'STARTUP' | 'BUSINESS_CONCEPT', string> = {
    STARTUP: 'Startups',
    BUSINESS_CONCEPT: 'Business Concepts',
  }

  return (
    <>
      <div className="space-y-6">
        {/* Header card */}
        <Card>
          <CardHeader className="flex flex-row items-start justify-between gap-4">
            <div className="flex-1 min-w-0">
              <CardTitle className="text-base">Latest Ranking Snapshot</CardTitle>
              {latestSnapshot && (
                <CardDescription className="mt-1 space-y-0.5">
                  <span>
                    Created{' '}
                    {new Date(latestSnapshot.createdAt).toLocaleString(undefined, {
                      dateStyle: 'medium',
                      timeStyle: 'short',
                    })}
                    {latestSnapshot.triggeredBy?.name && ` by ${latestSnapshot.triggeredBy.name}`}
                    {' · '}
                    {latestSnapshot.triggerType}
                  </span>
                  {latestSnapshot.criteriaText && (
                    <span className="block truncate text-xs">
                      Criteria: {latestSnapshot.criteriaText.slice(0, 120)}
                      {latestSnapshot.criteriaText.length > 120 ? '…' : ''}
                    </span>
                  )}
                </CardDescription>
              )}
            </div>
            <div className="flex items-center gap-2 flex-shrink-0">
              <Button
                size="sm"
                variant="outline"
                onClick={handleExportScores}
                disabled={exportLoading}
              >
                {exportLoading ? (
                  <Loader2 className="mr-2 h-4 w-4 animate-spin" />
                ) : (
                  <Download className="mr-2 h-4 w-4" />
                )}
                Export CSV
              </Button>
              <Button
                size="sm"
                variant="outline"
                onClick={() => triggerRankMutation.mutate({ roundId })}
                disabled={rankingInProgress}
              >
                {rankingInProgress ? (
                  <>
                    <Loader2 className="mr-2 h-4 w-4 animate-spin" />
                    Ranking&hellip;
                  </>
                ) : isFormulaMode ? (
                  <>
                    <Calculator className="mr-2 h-4 w-4" />
                    Run Ranking
                  </>
                ) : (
                  <>
                    <Sparkles className="mr-2 h-4 w-4" />
                    Run AI Ranking
                  </>
                )}
              </Button>
              {/* Advance Top N removed — use Finalization tab instead */}
            </div>
          </CardHeader>
        </Card>

        {/* Ranking Configuration: criteria text + weights */}
        <Collapsible open={weightsOpen} onOpenChange={setWeightsOpen}>
          <Card>
            <CollapsibleTrigger asChild>
              <CardHeader className="cursor-pointer flex flex-row items-center justify-between gap-4 hover:bg-muted/50 transition-colors">
                <div className="flex items-center gap-2">
                  <Settings2 className="h-4 w-4 text-muted-foreground" />
                  <div>
                    <CardTitle className="text-base">Ranking Configuration</CardTitle>
                    <CardDescription className="mt-0.5">Criteria text, per-criterion weights, and bias correction</CardDescription>
                  </div>
                </div>
                <ChevronDown className={cn('h-4 w-4 text-muted-foreground transition-transform', weightsOpen && 'rotate-180')} />
              </CardHeader>
            </CollapsibleTrigger>
            <CollapsibleContent>
              <CardContent className="space-y-5 pt-0">
                {/* Score vs Pass Rate weights */}
                <div className="space-y-3">
                  <div>
                    <Label>Formula Weights</Label>
                    <p className="text-xs text-muted-foreground">
                      Control the balance between evaluation scores and yes/no pass rate in the composite ranking
                    </p>
                  </div>
                  <div className="space-y-3">
                    <div className="flex items-center gap-4">
                      <span className="text-sm w-40 flex-shrink-0">Score Weight</span>
                      <Slider
                        min={0}
                        max={10}
                        step={1}
                        value={[localScoreWeight]}
                        onValueChange={([v]) => setLocalScoreWeight(v)}
                        className="flex-1"
                      />
                      <span className="text-sm font-mono w-6 text-right">{localScoreWeight}</span>
                    </div>
                    <div className="flex items-center gap-4">
                      <span className="text-sm w-40 flex-shrink-0">Pass Rate Weight</span>
                      <Slider
                        min={0}
                        max={10}
                        step={1}
                        value={[localPassRateWeight]}
                        onValueChange={([v]) => setLocalPassRateWeight(v)}
                        className="flex-1"
                      />
                      <span className="text-sm font-mono w-6 text-right">{localPassRateWeight}</span>
                    </div>
                  </div>
                </div>

                {/* Ranking criteria text (optional — triggers AI mode) */}
                <div className="space-y-2">
                  <Label htmlFor="rankingCriteria">AI Ranking Criteria (optional)</Label>
                  <p className="text-xs text-muted-foreground">
                    Optional: describe special ranking criteria for AI-assisted ranking.
                    Leave empty for formula-based ranking (faster, no AI cost).
                  </p>
                  <Textarea
                    id="rankingCriteria"
                    rows={3}
                    placeholder='E.g. "Prioritize innovation and ocean impact. Filter out projects with pass rate below 50%."'
                    value={localCriteriaText}
                    onChange={(e) => setLocalCriteriaText(e.target.value)}
                    className="resize-y"
                  />
                  {isFormulaMode ? (
                    <p className="text-xs text-emerald-600 flex items-center gap-1">
                      <Calculator className="h-3 w-3" /> Formula mode — ranking uses weights only, no AI calls
                    </p>
                  ) : (
                    <p className="text-xs text-amber-600 flex items-center gap-1">
                      <Sparkles className="h-3 w-3" /> AI mode — criteria will be parsed and used for ranking (uses API credits)
                    </p>
                  )}
                </div>

                {/* Per-criterion weights */}
                {numericCriteria.length > 0 && (
                  <div className="space-y-3">
                    <div>
                      <Label>Criteria Weights</Label>
                      <p className="text-xs text-muted-foreground">
                        Set relative importance of each evaluation criterion (0 = ignore, 10 = highest priority)
                      </p>
                    </div>
                    <div className="space-y-3">
                      {numericCriteria.map((c) => (
                        <div key={c.id} className="flex items-center gap-4">
                          <span className="text-sm w-40 truncate flex-shrink-0" title={c.label}>{c.label}</span>
                          <Slider
                            min={0}
                            max={10}
                            step={1}
                            value={[localWeights[c.id] ?? 1]}
                            onValueChange={([v]) => setLocalWeights((prev) => ({ ...prev, [c.id]: v }))}
                            className="flex-1"
                          />
                          <span className="text-sm font-mono w-6 text-right">{localWeights[c.id] ?? 1}</span>
                        </div>
                      ))}
                    </div>
                  </div>
                )}

                <div className="flex items-center gap-2 pt-2 border-t">
                  <Button
                    size="sm"
                    onClick={saveRankingConfig}
                    disabled={updateRoundMutation.isPending}
                  >
                    {updateRoundMutation.isPending ? <Loader2 className="h-4 w-4 mr-2 animate-spin" /> : null}
                    Save Configuration
                  </Button>
                  <p className="text-xs text-muted-foreground">
                    Weights are applied when ranking is run. Z-score normalization corrects for juror bias automatically.
                  </p>
                </div>
              </CardContent>
            </CollapsibleContent>
          </Card>
        </Collapsible>

        {/* Ranking in-progress banner */}
        {rankingInProgress && (
          <Card className="border-blue-200 bg-blue-50 dark:border-blue-800 dark:bg-blue-950/30">
            <CardContent className="flex items-center gap-3 py-4">
              <Loader2 className="h-5 w-5 animate-spin text-blue-600 dark:text-blue-400 flex-shrink-0" />
              <div className="flex-1 min-w-0">
                <p className="text-sm font-medium text-blue-900 dark:text-blue-200">
                  Ranking in progress&hellip;
                </p>
                <p className="text-xs text-blue-700 dark:text-blue-400">
                  This may take a minute. You can continue working — results will appear automatically.
                </p>
              </div>
              <div className="h-1.5 w-32 rounded-full bg-blue-200 dark:bg-blue-800 overflow-hidden flex-shrink-0">
                <div className="h-full w-full rounded-full bg-blue-500 animate-pulse" />
              </div>
            </CardContent>
          </Card>
        )}

        {/* Per-category sections */}
        {(['STARTUP', 'BUSINESS_CONCEPT'] as const).map((category) => (
          <Card key={category}>
            <CardHeader>
              <CardTitle className="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
                {categoryLabels[category]}
                {evalConfig && evalConfig.advanceMode === 'threshold' && evalConfig.advanceScoreThreshold != null ? (
                  <span className="ml-2 text-xs font-normal normal-case">
                    (Score &ge; {evalConfig.advanceScoreThreshold} advance)
                  </span>
                ) : evalConfig && (category === 'STARTUP' ? evalConfig.startupAdvanceCount : evalConfig.conceptAdvanceCount) > 0 ? (
                  <span className="ml-2 text-xs font-normal normal-case">
                    (Top {category === 'STARTUP' ? evalConfig.startupAdvanceCount : evalConfig.conceptAdvanceCount} advance)
                  </span>
                ) : null}
              </CardTitle>
            </CardHeader>
            <CardContent>
              {localOrder[category].length === 0 ? (
                <p className="text-sm text-muted-foreground">
                  No {category === 'STARTUP' ? 'startup' : 'business concept'} projects ranked.
                </p>
              ) : (() => {
                // Precompute cutoff index so it only shows ONCE
                const isThresholdMode = evalConfig?.advanceMode === 'threshold' && evalConfig.advanceScoreThreshold != null
                const advanceCount = isThresholdMode ? 0 : (category === 'STARTUP'
                  ? (evalConfig?.startupAdvanceCount ?? 0)
                  : (evalConfig?.conceptAdvanceCount ?? 0))
                const threshold = evalConfig?.advanceScoreThreshold ?? 0

                let cutoffIndex = -1
                if (isThresholdMode) {
                  // Find the FIRST project that does NOT meet the threshold — cutoff goes before it.
                  // Works correctly because localOrder is sorted by avgGlobalScore (the same metric).
                  const firstFailIdx = localOrder[category].findIndex((id) => {
                    const e = rankingMap.get(id)
                    return (e?.avgGlobalScore ?? 0) < threshold
                  })
                  if (firstFailIdx === -1) {
                    // All meet threshold — cutoff after the last one
                    cutoffIndex = localOrder[category].length - 1
                  } else if (firstFailIdx > 0) {
                    cutoffIndex = firstFailIdx - 1
                  }
                } else if (advanceCount > 0) {
                  cutoffIndex = advanceCount - 1
                }

                // Check if admin has reordered this category
                const reorders = (snapshot?.reordersJson as Array<{
                  category: 'STARTUP' | 'BUSINESS_CONCEPT'
                  orderedProjectIds: string[]
                }> | null) ?? []
                const hasReorders = reorders.some((r) => r.category === category)

                return (
                <DndContext
                  sensors={sensors}
                  collisionDetection={closestCenter}
                  onDragEnd={(event) => handleDragEnd(category, event)}
                >
                  <SortableContext
                    items={localOrder[category]}
                    strategy={verticalListSortingStrategy}
                  >
                    <AnimatePresence initial={false}>
                      <div className="space-y-2">
                        {localOrder[category].map((projectId, index) => {
                          const entry = rankingMap.get(projectId)
                          const projectAvg = entry?.avgGlobalScore ?? 0
                          const isAdvancing = isThresholdMode
                            ? projectAvg >= threshold
                            : (advanceCount > 0 && index < advanceCount)
                          const isCutoffRow = cutoffIndex >= 0 && index === cutoffIndex

                          return (
                            <React.Fragment key={projectId}>
                              <motion.div
                                layout
                                initial={{ opacity: 0, y: 20 }}
                                animate={{ opacity: 1, y: 0 }}
                                exit={{ opacity: 0, y: -20 }}
                                className={isAdvancing
                                  ? 'rounded-lg bg-emerald-50 border-l-4 border-emerald-400 dark:bg-emerald-950/20 dark:border-emerald-600'
                                  : ''}
                              >
                                <SortableProjectRow
                                  projectId={projectId}
                                  currentRank={index + 1}
                                  entry={rankingMap.get(projectId)}
                                  projectInfo={projectInfoMap.get(projectId)}
                                  jurorScores={evalScores?.[projectId]}
                                  onSelect={() => setSelectedProjectId(projectId)}
                                  isSelected={selectedProjectId === projectId}
                                  originalRank={hasReorders ? snapshotOrder[projectId] : undefined}
                                />
                              </motion.div>
                              {isCutoffRow && (
                                <div className="flex items-center gap-2 py-1">
                                  <div className="flex-1 border-t-2 border-dashed border-emerald-400/60" />
                                  <span className="text-xs font-medium text-emerald-600 dark:text-emerald-400 whitespace-nowrap">
                                    Advancement cutoff — {isThresholdMode ? `Score ≥ ${threshold}` : `Top ${advanceCount}`}
                                  </span>
                                  <div className="flex-1 border-t-2 border-dashed border-emerald-400/60" />
                                </div>
                              )}
                            </React.Fragment>
                          )
                        })}
                      </div>
                    </AnimatePresence>
                  </SortableContext>
                </DndContext>
                )
              })()}
            </CardContent>
          </Card>
        ))}
      </div>

      {/* Advance dialog removed — use Finalization tab */}

      {/* Side panel Sheet */}
      <Sheet
        open={!!selectedProjectId}
        onOpenChange={(open) => {
          if (!open) setSelectedProjectId(null)
        }}
      >
        <SheetContent className="w-[480px] sm:max-w-[480px] overflow-y-auto">
          <SheetHeader>
            <SheetTitle>{projectDetail?.project.title ?? 'Project Details'}</SheetTitle>
            <SheetDescription>
              {selectedProjectId ? `ID: …${selectedProjectId.slice(-8)}` : ''}
            </SheetDescription>
            {selectedProjectId && (
              <a
                href={`/admin/projects/${selectedProjectId}`}
                target="_blank"
                rel="noopener noreferrer"
                className="inline-flex items-center gap-1.5 text-sm text-primary hover:underline mt-1"
              >
                <ExternalLink className="h-3.5 w-3.5" />
                View Project Page
              </a>
            )}
          </SheetHeader>

          {detailLoading ? (
            <div className="mt-6 space-y-3">
              <Skeleton className="h-16 w-full" />
              <Skeleton className="h-24 w-full" />
              <Skeleton className="h-24 w-full" />
            </div>
          ) : projectDetail ? (
            <div className="mt-6 space-y-6">
              {/* Stats summary */}
              {projectDetail.stats && (
                <div className="grid grid-cols-3 gap-3">
                  <div className="rounded-lg border p-3 text-center">
                    <p className="text-xs text-muted-foreground">Avg Score</p>
                    <p className="mt-1 text-lg font-semibold">
                      {projectDetail.stats.averageGlobalScore?.toFixed(1) ?? '—'}
                    </p>
                  </div>
                  <div className="rounded-lg border p-3 text-center">
                    <p className="text-xs text-muted-foreground">Pass Rate</p>
                    <p className="mt-1 text-lg font-semibold">
                      {projectDetail.stats.totalEvaluations > 0
                        ? `${Math.round((projectDetail.stats.yesVotes / projectDetail.stats.totalEvaluations) * 100)}%`
                        : '—'}
                    </p>
                  </div>
                  <div className="rounded-lg border p-3 text-center">
                    <p className="text-xs text-muted-foreground">Evaluators</p>
                    <p className="mt-1 text-lg font-semibold">
                      {projectDetail.stats.totalEvaluations}
                    </p>
                  </div>
                </div>
              )}

              {/* Per-juror evaluations */}
              <div>
                <h4 className="mb-3 text-sm font-semibold">Juror Evaluations</h4>
                {(() => {
                  const submitted = projectDetail.assignments.filter(
                    (a) => a.evaluation?.status === 'SUBMITTED' && a.round.id === roundId,
                  )
                  if (submitted.length === 0) {
                    return (
                      <p className="text-sm text-muted-foreground">
                        No submitted evaluations for this round.
                      </p>
                    )
                  }
                  return (
                    <div className="space-y-3">
                      {submitted.map((a) => {
                        const isExpanded = expandedReviews.has(a.id)
                        return (
                          <div
                            key={a.id}
                            className="rounded-lg border p-3 cursor-pointer hover:bg-muted/50 transition-colors"
                            onClick={() => setExpandedReviews(prev => {
                              const next = new Set(prev)
                              next.has(a.id) ? next.delete(a.id) : next.add(a.id)
                              return next
                            })}
                          >
                            <div className="flex items-center justify-between">
                              <span className="font-medium text-sm">{a.user?.name ?? a.user?.email ?? 'Unknown'}</span>
                              <div className="flex items-center gap-2">
                                {a.evaluation?.binaryDecision != null && (
                                  <Badge
                                    variant={a.evaluation.binaryDecision ? 'default' : 'destructive'}
                                    className={a.evaluation.binaryDecision ? 'bg-emerald-100 text-emerald-700 hover:bg-emerald-100' : ''}
                                  >
                                    {a.evaluation.binaryDecision ? 'Yes' : 'No'}
                                  </Badge>
                                )}
                                <Badge variant="outline">Score: {a.evaluation?.globalScore?.toFixed(1) ?? '—'}</Badge>
                              </div>
                            </div>
                            {isExpanded && a.evaluation?.feedbackText && (
                              <p className="mt-2 text-sm text-muted-foreground whitespace-pre-wrap border-t pt-2">
                                {a.evaluation.feedbackText}
                              </p>
                            )}
                          </div>
                        )
                      })}
                    </div>
                  )
                })()}
              </div>
            </div>
          ) : null}
        </SheetContent>
      </Sheet>
    </>
  )
}
