import { cn } from '../../lib/utils'
import type { PipelineJob, RunStatus } from '../../types/api'

// ── Status maps ───────────────────────────────────────────────────────────────

const STATUS_COLOR: Record<RunStatus, string> = {
  queued:    'bg-[var(--c-surface-muted)] border-[var(--c-border)] text-[var(--c-muted)]',
  running:   'bg-[var(--c-brand-tint)] border-[var(--c-brand-focus)] text-[var(--c-brand)]',
  succeeded: 'bg-[#E3FCEF] border-[#79F2C0] text-[#006644]',
  failed:    'bg-[var(--c-danger-tint)] border-[#FF8F73] text-[var(--c-danger-dark)]',
  cancelled: 'bg-[var(--c-surface-muted)] border-[var(--c-border)] text-[var(--c-muted)]',
}

const STATUS_DOT: Record<RunStatus, string> = {
  queued:    'bg-[var(--c-subtle)]',
  running:   'bg-[var(--c-brand)] animate-pulse',
  succeeded: 'bg-[var(--c-success)]',
  failed:    'bg-[var(--c-danger)]',
  cancelled: 'bg-[var(--c-subtle)]',
}

const STATUS_LABEL: Record<RunStatus, string> = {
  queued:    'Queued',
  running:   'Running',
  succeeded: 'Passed',
  failed:    'Failed',
  cancelled: 'Cancelled',
}

// ── Props ─────────────────────────────────────────────────────────────────────

interface PipelineWaterfallProps {
  /** Jobs from a PipelineRun. Each job has a `needs` JSON array of dependency names. */
  jobs: PipelineJob[]
  /** Overall run status — used for the header badge. */
  runStatus: RunStatus
  runId: number
  /** Called when user clicks a job node. */
  onSelectJob?: (jobId: number) => void
  selectedJobId?: number | null
}

// ── DAG column builder ────────────────────────────────────────────────────────

function topoColumns(jobs: PipelineJob[]): PipelineJob[][] {
  const nameToJob = new Map(jobs.map(j => [j.name, j]))
  const depth = new Map<string, number>()

  function getDepth(name: string, visited = new Set<string>()): number {
    if (depth.has(name)) return depth.get(name)!
    if (visited.has(name)) return 0
    visited.add(name)
    const job = nameToJob.get(name)
    if (!job) return 0
    let needs: string[] = []
    try { needs = JSON.parse(job.needs || '[]') } catch { needs = [] }
    const d = needs.length === 0 ? 0 : 1 + Math.max(...needs.map(n => getDepth(n, new Set(visited))))
    depth.set(name, d)
    return d
  }

  jobs.forEach(j => getDepth(j.name))
  const maxDepth = Math.max(...Array.from(depth.values()), 0)
  const cols: PipelineJob[][] = Array.from({ length: maxDepth + 1 }, () => [])
  jobs.forEach(j => cols[depth.get(j.name) ?? 0].push(j))
  return cols.filter(c => c.length > 0)
}

function duration(start: string | null, end: string | null): string {
  if (!start) return ''
  const ms = new Date(end ?? Date.now()).getTime() - new Date(start).getTime()
  const s = Math.floor(ms / 1000)
  if (s < 60) return `${s}s`
  return `${Math.floor(s / 60)}m ${s % 60}s`
}

// ── Status icon ───────────────────────────────────────────────────────────────

function StatusIcon({ status }: { status: RunStatus }) {
  if (status === 'succeeded') {
    return (
      <svg width="16" height="16" fill="none" stroke="var(--c-success)" strokeWidth="2.5" viewBox="0 0 24 24">
        <path strokeLinecap="round" strokeLinejoin="round" d="m4.5 12.75 6 6 9-13.5" />
      </svg>
    )
  }
  if (status === 'failed') {
    return (
      <svg width="16" height="16" fill="none" stroke="var(--c-danger)" strokeWidth="2.5" viewBox="0 0 24 24">
        <path strokeLinecap="round" strokeLinejoin="round" d="M6 18 18 6M6 6l12 12" />
      </svg>
    )
  }
  if (status === 'running') {
    return (
      <svg width="16" height="16" fill="none" stroke="var(--c-brand)" strokeWidth="2" viewBox="0 0 24 24" className="animate-spin">
        <path strokeLinecap="round" d="M12 3a9 9 0 1 0 9 9" />
      </svg>
    )
  }
  if (status === 'cancelled') {
    return (
      <svg width="16" height="16" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24">
        <path strokeLinecap="round" strokeLinejoin="round" d="M18.364 18.364A9 9 0 0 0 5.636 5.636m12.728 12.728A9 9 0 0 1 5.636 5.636m12.728 12.728L5.636 5.636" />
      </svg>
    )
  }
  // queued
  return (
    <svg width="16" height="16" fill="none" stroke="currentColor" strokeWidth="1.5" viewBox="0 0 24 24">
      <circle cx="12" cy="12" r="9" />
    </svg>
  )
}

// ── Component ─────────────────────────────────────────────────────────────────

export function PipelineWaterfall({
  jobs,
  runStatus,
  runId,
  onSelectJob,
  selectedJobId,
}: PipelineWaterfallProps) {
  const columns = topoColumns(jobs)

  return (
    <div className="border border-[var(--c-border)] rounded p-4 bg-[var(--c-surface)]">
      {/* Header */}
      <div className="flex items-center justify-between mb-4">
        <div className="flex items-center gap-2">
          <span className={cn('w-2.5 h-2.5 rounded-full shrink-0', STATUS_DOT[runStatus])} />
          <span className="text-sm font-semibold text-[var(--c-text)]">
            Run #{runId}
          </span>
          <span className={cn(
            'text-xs font-medium px-2 py-0.5 rounded-full border',
            STATUS_COLOR[runStatus],
          )}>
            {STATUS_LABEL[runStatus]}
          </span>
        </div>
        <span className="text-xs text-[var(--c-muted)]">
          {jobs.length} job{jobs.length !== 1 ? 's' : ''}
        </span>
      </div>

      {/* DAG waterfall */}
      {columns.length === 0 ? (
        <p className="text-xs text-[var(--c-muted)] text-center py-4">No jobs.</p>
      ) : (
        <div className="flex items-center gap-0 overflow-x-auto pb-2">
          {columns.map((col, colIdx) => (
            <div key={colIdx} className="flex items-center shrink-0">
              {/* Column */}
              <div className="flex flex-col gap-2">
                {col.map(job => {
                  const status = (job.status as RunStatus) || 'queued'
                  const isSelected = selectedJobId === job.id
                  return (
                    <button
                      key={job.id}
                      onClick={() => onSelectJob?.(job.id)}
                      className={cn(
                        'flex flex-col items-center justify-center px-4 py-3 rounded border text-center min-w-[88px] transition-all',
                        STATUS_COLOR[status],
                        onSelectJob ? 'cursor-pointer' : 'cursor-default',
                        isSelected ? 'ring-2 ring-[var(--c-brand)] ring-offset-1' : '',
                      )}
                    >
                      <StatusIcon status={status} />
                      <span className="text-[11px] font-semibold mt-1 truncate max-w-[72px]">{job.name}</span>
                      {(job.startedAt || job.finishedAt) && (
                        <span className="text-[10px] opacity-70 mt-0.5">
                          {duration(job.startedAt, job.finishedAt)}
                        </span>
                      )}
                    </button>
                  )
                })}
              </div>

              {/* Connector (not after last column) */}
              {colIdx < columns.length - 1 && (
                <div className="flex items-center px-1">
                  <div className="h-px w-4 bg-[var(--c-border)]" />
                  <svg width="6" height="8" viewBox="0 0 6 8" fill="var(--c-border)">
                    <path d="M0 0l6 4-6 4V0z" />
                  </svg>
                </div>
              )}
            </div>
          ))}
        </div>
      )}
    </div>
  )
}
