import { memo } from "react"
import { Handle, Position, type NodeProps } from "@xyflow/react"
import { Flag, PackageCheck, Timer } from "lucide-react"

import { cn } from "@/lib/utils"
import { ProductionRunStatus } from "@/types/production"
import { STAGE_STATUS_COLOR, STAGE_STATUS_LABEL, type StageStatus } from "@/lib/production-status-colors"

export interface RunHeaderData extends Record<string, unknown> {
  docNo: string
  templateName: string
  status: ProductionRunStatus
}

/** Left-most box on a run's production line — the run itself, not a stage. */
function RunHeaderNode({ data }: NodeProps & { data: RunHeaderData }) {
  return (
    <div className="flex w-48 flex-col gap-1.5 rounded-2xl bg-card p-3 shadow-sm ring-1 ring-foreground/10">
      <span className="truncate text-xs font-medium text-muted-foreground">{data.docNo}</span>
      <p className="truncate text-sm font-bold text-foreground">{data.templateName}</p>
      <Handle type="source" position={Position.Right} className="!bg-primary !size-2.5" />
    </div>
  )
}

export interface RunStageData extends Record<string, unknown> {
  name: string
  roleLabel: string | null
  state: StageStatus
  isTerminal: boolean
  isEntry: boolean
  estimatedMinutes: number
  /** Whole minutes once finished; null while still running (FR-MFG-19). */
  actualMinutes: number | null
  /** Set once started — drives the "running" hint while actualMinutes is still null. */
  actualStartAt: string | null
  /**
   * Aggregate intake across this stage's Upstream inputs, or null when it has none (an entry
   * stage draws entirely from stock). `delivered >= planned` is exactly the readiness rule the
   * server applies, so the badge doubles as an explanation of why a stage is still Waiting.
   */
  intake: { delivered: number; planned: number } | null
  /** Σ availableToTransfer across outputs — WIP produced but not yet pushed downstream. */
  availableToTransfer: number
  /** Marks the stage the operator is expected to act on next. */
  isActive: boolean
}

function fmt(n: number): string {
  // Quantities are decimal(18,4) server-side; trailing zeros just add noise on a canvas card.
  return Number(n.toFixed(4)).toLocaleString()
}

/**
 * One stage on a run's production line, positioned from the run's own `posX`/`posY` (copied
 * from the template at creation) and colored by its live status. Clicking it opens the stage
 * drawer — handled by the page via `onNodeClick`, not here.
 */
function RunStageNode({ data, selected }: NodeProps & { data: RunStageData }) {
  const color = STAGE_STATUS_COLOR[data.state]
  const running = data.actualStartAt !== null && data.actualMinutes === null
  const intakeShort = data.intake !== null && data.intake.delivered < data.intake.planned

  return (
    <div
      className={cn(
        "w-52 cursor-pointer rounded-2xl bg-card p-3 shadow-sm ring-2 transition-all hover:ring-primary/40",
        selected ? "ring-primary" : "ring-foreground/10"
      )}
      style={data.isActive && !selected ? { boxShadow: `0 0 0 3px ${color}` } : undefined}
    >
      <Handle type="target" position={Position.Left} className="!bg-primary !size-2.5" />

      <div className="flex items-start gap-1.5">
        <span className="mt-1 size-2.5 shrink-0 rounded-full" style={{ backgroundColor: color }} />
        <p className="min-w-0 flex-1 truncate text-sm font-bold text-foreground">{data.name}</p>
        {data.isTerminal && (
          <span title="Final stage" className="mt-0.5 shrink-0 text-muted-foreground">
            <Flag className="size-3.5" />
          </span>
        )}
      </div>

      <div className="mt-1 flex flex-wrap items-center gap-1.5">
        <span className="text-xs font-medium" style={{ color }}>
          {STAGE_STATUS_LABEL[data.state]}
        </span>
        {data.roleLabel && (
          <span className="rounded-full bg-primary/10 px-1.5 py-0.5 text-xs font-medium text-primary">{data.roleLabel}</span>
        )}
      </div>

      <div className="mt-2 flex items-center gap-1 text-xs text-muted-foreground">
        <Timer className="size-3" />
        {data.actualMinutes !== null
          ? `${data.actualMinutes} / ${data.estimatedMinutes} min`
          : running
            ? `running · est. ${data.estimatedMinutes} min`
            : `est. ${data.estimatedMinutes} min`}
      </div>

      {data.intake && (
        <div
          className={cn(
            "mt-1.5 rounded-md px-1.5 py-0.5 text-xs font-medium",
            intakeShort ? "bg-warning/10 text-warning" : "bg-success/10 text-success"
          )}
        >
          Intake {fmt(data.intake.delivered)} / {fmt(data.intake.planned)}
        </div>
      )}

      {data.availableToTransfer > 0 && (
        <div className="mt-1.5 flex items-center gap-1 rounded-md bg-info/10 px-1.5 py-0.5 text-xs font-medium text-info">
          <PackageCheck className="size-3" />
          {fmt(data.availableToTransfer)} to transfer
        </div>
      )}

      <Handle type="source" position={Position.Right} className="!bg-primary !size-2.5" />
    </div>
  )
}

export const RunHeaderNodeComponent = memo(RunHeaderNode)
export const RunStageNodeComponent = memo(RunStageNode)
