import { useEffect, useState } from 'react'
import { DetailDrawer, LinkButton, safeHttpUrl, StatusChip, VerdictBadge, type Verdict } from '@overdeck/deck-ui'
import {
  attemptInProgress,
  isUsageUnknown,
  shortAttemptId,
  wrapperLabel,
  type AttemptRecord,
} from './attempt-record'

function verdictForAttempt(attempt: AttemptRecord): Verdict {
  if (attempt.valid === true) return 'works'
  if (attempt.valid === false) return 'broken'
  const verdict = attempt.verdict?.trim().toLowerCase()
  if (['pass', 'passed', 'works', 'valid', 'succeeded'].includes(verdict ?? '')) return 'works'
  if (['fail', 'failed', 'broken', 'invalid', 'error'].includes(verdict ?? '')) return 'broken'
  if (verdict === 'missing') return 'missing'
  return 'unverified'
}

function unavailable(label: string) {
  return <span className="text-fg-muted">{label}</span>
}

function formatCostUsd(costUsd: number) {
  return new Intl.NumberFormat('en-US', {
    style: 'currency',
    currency: 'USD',
    minimumFractionDigits: 4,
    maximumFractionDigits: 4,
  }).format(costUsd)
}

function usageDetail(usage: AttemptRecord['usage']) {
  if (!usage) return unavailable('not yet available')
  if (isUsageUnknown(usage)) {
    return (
      <span>
        unmetered <span className="text-fg-muted">({usage.model})</span>
      </span>
    )
  }
  const parts = [`${usage.inputTokens} in · ${usage.outputTokens} out`]
  if (usage.costUsd !== undefined && usage.costUsd > 0) {
    parts.push(formatCostUsd(usage.costUsd))
  }
  if (usage.cachedTokens !== undefined && usage.cachedTokens > 0) {
    parts.push(`${usage.cachedTokens} cached`)
  }
  return (
    <span>
      {parts.join(' · ')}
      <span className="text-fg-muted"> · {usage.model}</span>
    </span>
  )
}

function activityDetail(activity: AttemptRecord['activity']) {
  if (!activity) return unavailable('not yet available')
  return (
    <dl className="grid grid-cols-[auto_1fr] gap-x-3 gap-y-1 text-sm">
      <dt className="text-fg-subtle">Tool calls</dt>
      <dd>{activity.toolCalls ?? 0}</dd>
      <dt className="text-fg-subtle">Files touched</dt>
      <dd>{activity.filesTouched?.length ?? 0}</dd>
      <dt className="text-fg-subtle">Repeated commands</dt>
      <dd>{activity.repeatedCommands?.length ?? 0}</dd>
      {activity.failureFingerprint && (
        <>
          <dt className="text-fg-subtle">Failure fingerprint</dt>
          <dd className="font-mono text-xs">{activity.failureFingerprint}</dd>
        </>
      )}
    </dl>
  )
}

type ArtifactLoadState =
  | { href: undefined; status: 'idle' }
  | { href: string; status: 'loading' }
  | { href: string; status: 'ready'; content: string }
  | { href: string; status: 'unavailable' }

function useArtifactContent(href: string | undefined, enabled: boolean): ArtifactLoadState {
  const [state, setState] = useState<ArtifactLoadState>({ href: undefined, status: 'idle' })
  const activeHref = enabled ? href : undefined

  useEffect(() => {
    if (!enabled || !href) {
      setState({ href: undefined, status: 'idle' })
      return
    }

    let cancelled = false
    setState({ href, status: 'loading' })

    void fetch(href)
      .then(async (response) => {
        if (!response.ok) throw new Error(`artifact unavailable: ${response.status}`)
        const content = await response.text()
        if (!cancelled) {
          setState(content.trim() ? { href, status: 'ready', content } : { href, status: 'unavailable' })
        }
      })
      .catch(() => {
        if (!cancelled) setState({ href, status: 'unavailable' })
      })

    return () => {
      cancelled = true
    }
  }, [href, enabled])

  if (state.href !== activeHref) {
    return activeHref ? { href: activeHref, status: 'loading' } : { href: undefined, status: 'idle' }
  }

  return state
}

function artifactPre(content: string, testId: string) {
  return (
    <pre
      className="mt-2 max-h-64 overflow-auto rounded-md border border-border bg-surface-raised p-3 text-xs text-fg-muted"
      data-testid={testId}
    >
      {content}
    </pre>
  )
}

function collapsedPrompt(content: string, expanded: boolean, onToggle: () => void) {
  return (
    <div className="mt-2">
      <button
        type="button"
        onClick={onToggle}
        className="min-h-11 rounded-md px-3 text-xs font-semibold text-fg-muted hover:bg-surface-raised"
        aria-expanded={expanded}
      >
        {expanded ? 'Collapse prompt' : 'Expand prompt'}
      </button>
      {expanded && artifactPre(content, 'attempt-drawer-prompt-content')}
    </div>
  )
}

export function AttemptDetailDrawer(props: {
  attempt: AttemptRecord | null
  open: boolean
  onClose(): void
  promptContent?: string | null
  replyContent?: string | null
  promptHref?: string
  replyHref?: string
}) {
  const [promptExpanded, setPromptExpanded] = useState(false)
  const open = props.open && props.attempt !== null
  const promptLoad = useArtifactContent(props.promptHref, open && !props.promptContent)
  const replyLoad = useArtifactContent(props.replyHref, open && !props.replyContent)

  useEffect(() => {
    setPromptExpanded(false)
  }, [props.attempt?.attemptId])

  if (!props.open || !props.attempt) return null
  const attempt = props.attempt
  const inProgress = attemptInProgress(attempt)
  const promptContent =
    props.promptContent ??
    (promptLoad.status === 'ready' ? promptLoad.content : null)
  const replyContent =
    props.replyContent ??
    (replyLoad.status === 'ready' ? replyLoad.content : null)

  return (
    <DetailDrawer
      eyebrow="Attempt detail"
      title={`${attempt.task ?? 'task'} · ${shortAttemptId(attempt.attemptId)}`}
      titleId="attempt-detail-title"
      onClose={props.onClose}
    >
      <dl className="mt-5 grid grid-cols-[7rem_1fr] gap-x-4 gap-y-3 text-sm">
        <dt className="font-semibold text-fg-subtle">Phase</dt>
        <dd>{attempt.phase ?? 'not recorded'}</dd>
        <dt className="font-semibold text-fg-subtle">Seat</dt>
        <dd>{attempt.seat ?? 'not recorded'}</dd>
        <dt className="font-semibold text-fg-subtle">Model</dt>
        <dd>{attempt.model ?? 'not recorded'}</dd>
        <dt className="font-semibold text-fg-subtle">Wrapper</dt>
        <dd>{wrapperLabel(attempt.wrapper)}</dd>
        <dt className="font-semibold text-fg-subtle">Causal input</dt>
        <dd className="font-mono text-xs">{attempt.causalInput ?? 'not recorded'}</dd>
        <dt className="font-semibold text-fg-subtle">Verdict</dt>
        <dd data-testid="attempt-drawer-verdict">
          {inProgress ? <StatusChip status="running" /> : <VerdictBadge verdict={verdictForAttempt(attempt)} />}
        </dd>
        <dt className="font-semibold text-fg-subtle">Usage</dt>
        <dd data-testid="attempt-drawer-usage">{usageDetail(attempt.usage)}</dd>
        {attempt.failureClass && (
          <>
            <dt className="font-semibold text-fg-subtle">Failure class</dt>
            <dd data-testid="attempt-drawer-failure-class">{attempt.failureClass}</dd>
          </>
        )}
      </dl>

      <section className="mt-6 border-t border-border pt-5">
        <h3 className="text-sm font-semibold text-fg">Prompt</h3>
        {promptContent ? (
          collapsedPrompt(promptContent, promptExpanded, () => setPromptExpanded((value) => !value))
        ) : promptLoad.status === 'loading' ? (
          <p className="mt-2 text-sm text-fg-muted" data-testid="attempt-drawer-prompt-loading">
            Loading prompt…
          </p>
        ) : promptLoad.status === 'unavailable' ? (
          <p className="mt-2 text-sm text-fg-muted" data-testid="attempt-drawer-prompt-not-captured">
            not captured
          </p>
        ) : attempt.promptPath ? (
          props.promptHref ? null : (
            <p className="mt-2 text-sm text-fg-muted" data-testid="attempt-drawer-prompt-path">
              {attempt.promptPath}
            </p>
          )
        ) : (
          <p className="mt-2 text-sm text-fg-muted">
            {attempt.promptSha ? `sha256:${attempt.promptSha.slice(0, 12)}…` : 'not yet available'}
          </p>
        )}
        {props.promptHref && (
          <LinkButton href={safeHttpUrl(props.promptHref)} className="mt-2" data-testid="attempt-drawer-prompt-link">
            Open raw
          </LinkButton>
        )}
      </section>

      <section className="mt-6 border-t border-border pt-5">
        <h3 className="text-sm font-semibold text-fg">Reply</h3>
        {replyContent ? (
          artifactPre(replyContent, 'attempt-drawer-reply-content')
        ) : replyLoad.status === 'loading' ? (
          <p className="mt-2 text-sm text-fg-muted" data-testid="attempt-drawer-reply-loading">
            Loading reply…
          </p>
        ) : replyLoad.status === 'unavailable' ? (
          <p className="mt-2 text-sm text-fg-muted" data-testid="attempt-drawer-reply-not-captured">
            not captured
          </p>
        ) : attempt.replyPath ? (
          props.replyHref ? null : (
            <p className="mt-2 text-sm text-fg-muted" data-testid="attempt-drawer-reply-path">
              {attempt.replyPath}
            </p>
          )
        ) : (
          <p className="mt-2 text-sm text-fg-muted" data-testid="attempt-drawer-reply-missing">
            {inProgress ? 'not yet available' : 'no reply recorded'}
          </p>
        )}
        {props.replyHref && (
          <LinkButton href={safeHttpUrl(props.replyHref)} className="mt-2" data-testid="attempt-drawer-reply-link">
            Open raw
          </LinkButton>
        )}
      </section>

      <section className="mt-6 border-t border-border pt-5">
        <h3 className="text-sm font-semibold text-fg">Activity</h3>
        <div className="mt-2" data-testid="attempt-drawer-activity">
          {activityDetail(attempt.activity)}
        </div>
      </section>
    </DetailDrawer>
  )
}
