import { useEffect, useMemo, useRef, useState } from 'react'
import { skillsApi } from '../../api/skills'
import { mcpApi } from '../../api/mcp'
import {
  sessionsApi,
  type SessionContextSnapshot,
  type SessionInspectionResponse,
  type SessionUsageSnapshot,
} from '../../api/sessions'
import { useTranslation, type TranslationKey } from '../../i18n'
import { useUIStore } from '../../stores/uiStore'
import { SETTINGS_TAB_ID, useTabStore } from '../../stores/tabStore'
import { useMcpStore } from '../../stores/mcpStore'
import { useSkillStore } from '../../stores/skillStore'
import type { McpServerRecord } from '../../types/mcp'
import type { SkillMeta } from '../../types/skill'
import type { SlashCommandOption } from './composerUtils'

export type LocalSlashCommandName = 'mcp' | 'skills' | 'help' | 'status' | 'cost' | 'context'

type Props = {
  command: LocalSlashCommandName
  sessionId?: string
  cwd?: string
  commands?: SlashCommandOption[]
  onClose: () => void
}

type SessionInspectorTab = 'status' | 'usage' | 'context'
type Translate = ReturnType<typeof useTranslation>

function toneForStatus(status: McpServerRecord['status']) {
  switch (status) {
    case 'connected':
      return 'bg-[var(--color-inspector-success-bg)] text-[var(--color-inspector-success)] border-[var(--color-inspector-border)]'
    case 'needs-auth':
      return 'bg-[var(--color-surface-container-low)] text-[var(--color-warning)] border-[var(--color-border)]'
    case 'failed':
      return 'bg-[var(--color-inspector-danger-bg)] text-[var(--color-inspector-danger)] border-[var(--color-inspector-border)]'
    case 'disabled':
      return 'bg-[var(--color-surface-hover)] text-[var(--color-text-secondary)] border-[var(--color-border)]'
    default:
      return ''
  }
}

function scopeLabel(scope: string, t: ReturnType<typeof useTranslation>) {
  switch (scope) {
    case 'user':
      return t('settings.mcp.scope.user')
    case 'local':
      return t('settings.mcp.scope.local')
    case 'project':
      return t('settings.mcp.scope.project')
    default:
      return scope
  }
}

function projectBadge(path?: string, t?: ReturnType<typeof useTranslation>) {
  if (!path || !t) return null
  const label = path.replace(/\/$/, '').split('/').pop() || path
  return t('slash.mcp.projectBadge', { name: label })
}

function PanelShell({
  title,
  subtitle,
  children,
  onClose,
}: {
  title: string
  subtitle: string
  children: React.ReactNode
  onClose: () => void
}) {
  return (
    <div className="absolute bottom-full left-0 right-0 z-50 mb-3 overflow-hidden rounded-2xl border border-[var(--color-border)] bg-[var(--color-surface-container-lowest)] shadow-[var(--shadow-dropdown)]">
      <div className="flex items-start justify-between gap-4 border-b border-[var(--color-border)] px-5 py-4">
        <div>
          <h3 className="text-lg font-semibold text-[var(--color-text-primary)]">{title}</h3>
          <p className="mt-1 text-sm text-[var(--color-text-tertiary)]">{subtitle}</p>
        </div>
        <button
          type="button"
          onClick={onClose}
          className="flex h-9 w-9 items-center justify-center rounded-full text-[var(--color-text-secondary)] transition-colors hover:bg-[var(--color-surface-hover)] hover:text-[var(--color-text-primary)]"
        >
          <span className="material-symbols-outlined text-[18px]">close</span>
        </button>
      </div>
      <div className="max-h-[min(620px,72vh)] overflow-y-auto px-5 py-4">{children}</div>
    </div>
  )
}

function LoadingState({ label }: { label: string }) {
  return (
    <div className="flex items-center justify-center py-12 text-sm text-[var(--color-text-tertiary)]">
      <div className="mr-3 h-5 w-5 animate-spin rounded-full border-2 border-[var(--color-brand)] border-t-transparent" />
      {label}
    </div>
  )
}

function EmptyState({ title, body }: { title: string; body: string }) {
  return (
    <div className="rounded-2xl border border-dashed border-[var(--color-border)] bg-[var(--color-surface)] px-5 py-10 text-center">
      <div className="text-sm font-semibold text-[var(--color-text-primary)]">{title}</div>
      <div className="mt-2 text-xs leading-6 text-[var(--color-text-tertiary)]">{body}</div>
    </div>
  )
}

function ErrorState({ message }: { message: string }) {
  return (
    <div className="rounded-2xl border border-[var(--color-inspector-border)] bg-[var(--color-inspector-panel)] px-5 py-4 text-sm text-[var(--color-inspector-danger)]">
      {message}
    </div>
  )
}

function formatNumber(value: number | undefined) {
  return new Intl.NumberFormat().format(value ?? 0)
}

function formatDuration(seconds: number | undefined) {
  const total = Math.max(0, Math.round(seconds ?? 0))
  if (total < 60) return `${total}s`
  const minutes = Math.floor(total / 60)
  const remaining = total % 60
  return remaining ? `${minutes}m ${remaining}s` : `${minutes}m`
}

function formatPercent(value: number | undefined) {
  const percent = Math.max(0, Math.min(100, value ?? 0))
  return `${percent.toFixed(percent >= 10 || Number.isInteger(percent) ? 0 : 1)}%`
}

function sessionInspectorInitialTab(command: LocalSlashCommandName): SessionInspectorTab {
  if (command === 'cost') return 'usage'
  if (command === 'context') return 'context'
  return 'status'
}

function isRecord(value: unknown): value is Record<string, unknown> {
  return Boolean(value && typeof value === 'object')
}

function isSessionInspectionResponse(value: unknown): value is SessionInspectionResponse {
  if (!isRecord(value)) return false
  if (typeof value.active !== 'boolean') return false
  if (!isRecord(value.status)) return false
  return (
    typeof value.status.sessionId === 'string' &&
    typeof value.status.workDir === 'string' &&
    typeof value.status.permissionMode === 'string'
  )
}

function assertSessionInspectionResponse(value: unknown, t: Translate): SessionInspectionResponse {
  if (isSessionInspectionResponse(value)) return value
  throw new Error(t('slash.inspector.error.unavailable'))
}

function InspectorSectionTitle({ children, action }: { children: React.ReactNode; action?: React.ReactNode }) {
  return (
    <div className="mb-3 flex items-center justify-between gap-4">
      <div className="font-mono text-[12px] font-semibold uppercase tracking-[0.24em] text-[var(--color-inspector-heading)]">{children}</div>
      {action}
    </div>
  )
}

function MetricCard({ label, value, detail }: { label: string; value: React.ReactNode; detail?: React.ReactNode }) {
  return (
    <div className="min-h-[82px] rounded-md border border-[var(--color-inspector-border)] bg-[var(--color-inspector-panel)] px-4 py-4 font-mono">
      <div className="text-[12px] uppercase tracking-[0.2em] text-[var(--color-inspector-heading)]">{label}</div>
      <div className="mt-3 whitespace-pre-line text-[15px] leading-6 text-[var(--color-inspector-text)]">{value}</div>
      {detail && <div className="mt-1 text-[13px] leading-5 text-[var(--color-inspector-muted)]">{detail}</div>}
    </div>
  )
}

function InspectorNotice({ children }: { children: React.ReactNode }) {
  return (
    <div className="flex items-center gap-3 rounded-md border border-[var(--color-inspector-border)] bg-[var(--color-inspector-surface)] px-4 py-3 text-[14px] text-[var(--color-inspector-heading)]">
      <span className="material-symbols-outlined text-[18px] text-[var(--color-inspector-muted)]">info</span>
      <span>{children}</span>
    </div>
  )
}

function KeyValueRows({ rows }: { rows: Array<[string, React.ReactNode]> }) {
  return (
    <div className="overflow-hidden rounded-md border border-[var(--color-inspector-border)] bg-[var(--color-inspector-surface)] font-mono">
      {rows.map(([label, value]) => (
        <div key={label} className="grid grid-cols-[220px_minmax(0,1fr)] border-t border-[var(--color-inspector-border)] first:border-t-0">
          <div className="border-r border-[var(--color-inspector-border)] bg-[var(--color-inspector-panel)] px-4 py-3 text-[12px] font-semibold uppercase tracking-[0.24em] text-[var(--color-inspector-heading)]">
            {label}
          </div>
          <div className="min-w-0 break-words px-4 py-3 text-[14px] text-[var(--color-inspector-text)]">{value}</div>
        </div>
      ))}
    </div>
  )
}

function UsageTab({
  usage,
  context,
  error,
  t,
}: {
  usage?: SessionUsageSnapshot
  context?: SessionContextSnapshot
  error?: string
  t: Translate
}) {
  if (error && !usage) return <ErrorState message={error} />
  if (!usage) {
    return <EmptyState title={t('slash.inspector.usage.emptyTitle')} body={t('slash.inspector.usage.emptyBody')} />
  }

  const usageHasTokens = (
    usage.totalInputTokens +
    usage.totalOutputTokens +
    usage.totalCacheReadInputTokens +
    usage.totalCacheCreationInputTokens
  ) > 0
  const apiUsage = context?.apiUsage
  const useContextUsageFallback = !usageHasTokens && !!apiUsage
  const totalInputTokens = useContextUsageFallback ? apiUsage.input_tokens : usage.totalInputTokens
  const totalOutputTokens = useContextUsageFallback ? apiUsage.output_tokens : usage.totalOutputTokens
  const totalCacheReadInputTokens = useContextUsageFallback ? apiUsage.cache_read_input_tokens : usage.totalCacheReadInputTokens
  const totalCacheCreationInputTokens = useContextUsageFallback ? apiUsage.cache_creation_input_tokens : usage.totalCacheCreationInputTokens
  const models = Array.isArray(usage.models) && usage.models.length > 0
    ? usage.models
    : useContextUsageFallback
      ? [{
          model: context?.model ?? 'current-model',
          displayName: context?.model ?? t('slash.inspector.status.activeModel'),
          inputTokens: totalInputTokens,
          outputTokens: totalOutputTokens,
          cacheReadInputTokens: totalCacheReadInputTokens,
          cacheCreationInputTokens: totalCacheCreationInputTokens,
          webSearchRequests: 0,
          costUSD: 0,
          costDisplay: 'n/a',
          contextWindow: context?.rawMaxTokens ?? 0,
          maxOutputTokens: 0,
        }]
      : []
  const sourceLabel = useContextUsageFallback
    ? t('slash.inspector.usage.source.contextSnapshot')
    : usage.source === 'transcript'
      ? t('slash.inspector.usage.source.transcript')
      : t('slash.inspector.usage.source.currentProcess')

  return (
    <div className="space-y-7">
      {useContextUsageFallback && (
        <InspectorNotice>
          {t('slash.inspector.usage.contextSnapshotNotice')}
        </InspectorNotice>
      )}
      {usage.source === 'transcript' && (
        <div className="rounded-md border border-[var(--color-inspector-border)] bg-[var(--color-inspector-surface)] px-4 py-3 text-sm text-[var(--color-inspector-muted-strong)]">
          {t('slash.inspector.usage.transcriptNotice')}
        </div>
      )}
      {usage.hasUnknownModelCost && (
        <div className="rounded-xl border border-[var(--color-inspector-border)] bg-[var(--color-inspector-panel)] px-4 py-3 text-sm text-[var(--color-warning)]">
          {t('slash.inspector.usage.unknownCost')}
        </div>
      )}
      <div className="grid grid-cols-2 gap-4 lg:grid-cols-4">
        <MetricCard label={t('slash.inspector.usage.totalCost')} value={useContextUsageFallback ? 'n/a' : usage.costDisplay} />
        <MetricCard label={t('slash.inspector.usage.source')} value={sourceLabel} />
        <MetricCard label={t('slash.inspector.usage.apiDuration')} value={usage.source === 'transcript' || useContextUsageFallback ? '0ms' : formatDuration(usage.totalAPIDuration)} />
        <MetricCard label={usage.source === 'transcript' ? t('slash.inspector.usage.usageSpan') : t('slash.inspector.usage.wallDuration')} value={useContextUsageFallback ? '0ms' : formatDuration(usage.totalDuration)} />
        <MetricCard
          label={t('slash.inspector.usage.codeChanges')}
          value={`${formatNumber(usage.totalLinesAdded)}/${formatNumber(usage.totalLinesRemoved)}`}
        />
        <MetricCard label={t('slash.inspector.usage.input')} value={formatNumber(totalInputTokens)} />
        <MetricCard label={t('slash.inspector.usage.output')} value={formatNumber(totalOutputTokens)} />
        <MetricCard label={t('slash.inspector.usage.cacheReadWrite')} value={`${formatNumber(totalCacheReadInputTokens)} / ${formatNumber(totalCacheCreationInputTokens)}`} />
        <MetricCard label={t('slash.inspector.usage.webSearch')} value={formatNumber(usage.totalWebSearchRequests)} />
      </div>
      <section>
        <div className="mb-3 text-[22px] font-semibold text-[var(--color-inspector-text)]">{t('slash.inspector.usage.byModel')}</div>
        {models.length === 0 ? (
          <EmptyState title={t('slash.inspector.usage.noModelTitle')} body={t('slash.inspector.usage.noModelBody')} />
        ) : (
          <div className="overflow-hidden rounded-md border border-[var(--color-inspector-border)] bg-[var(--color-inspector-surface)] font-mono">
            {models.map((model) => (
              <div key={model.model} className="border-t border-[var(--color-inspector-border)] first:border-t-0">
                <div className="grid grid-cols-[minmax(0,1fr)_120px] items-center gap-4 border-b border-[var(--color-inspector-border)] px-4 py-3">
                  <div className="min-w-0">
                    <div className="truncate text-[13px] font-semibold text-[var(--color-inspector-text)]">{model.displayName || model.model}</div>
                    {(model.contextWindow > 0 || context?.rawMaxTokens) && (
                      <div className="mt-1 truncate text-[11px] text-[var(--color-inspector-muted)]">
                        {t('slash.inspector.status.contextWindow')}: {formatNumber(model.contextWindow || context?.rawMaxTokens)}
                      </div>
                    )}
                  </div>
                  <div className="text-right text-[12px] font-semibold uppercase tracking-[0.18em] text-[var(--color-inspector-heading)]">{t('slash.inspector.usage.tokens')}</div>
                </div>
                <div className="grid grid-cols-[160px_minmax(0,1fr)_120px] items-center gap-4 border-b border-[var(--color-inspector-border)] px-4 py-3 last:border-b-0">
                  <div className="text-[12px] uppercase tracking-[0.18em] text-[var(--color-inspector-heading)]">{t('slash.inspector.usage.input')}</div>
                  <div className="h-1 overflow-hidden rounded-full bg-[var(--color-inspector-chip)]">
                    <div className="h-full rounded-full bg-[var(--color-inspector-accent)]" style={{ width: '95%' }} />
                  </div>
                  <div className="text-right text-[13px] text-[var(--color-inspector-text)]">{formatNumber(model.inputTokens)}</div>
                </div>
                <div className="grid grid-cols-[160px_minmax(0,1fr)_120px] items-center gap-4 px-4 py-3">
                  <div className="text-[12px] uppercase tracking-[0.18em] text-[var(--color-inspector-heading)]">{t('slash.inspector.usage.output')}</div>
                  <div className="h-1 overflow-hidden rounded-full bg-[var(--color-inspector-chip)]">
                    <div className="h-full rounded-full bg-[var(--color-inspector-accent-secondary)]" style={{ width: `${Math.max(4, Math.min(100, (model.outputTokens / Math.max(1, model.inputTokens)) * 100))}%` }} />
                  </div>
                  <div className="text-right text-[13px] text-[var(--color-inspector-text)]">{formatNumber(model.outputTokens)}</div>
                </div>
              </div>
            ))}
          </div>
        )}
      </section>
    </div>
  )
}

type ContextCategory = SessionContextSnapshot['categories'][number]
type ContextMemoryFile = SessionContextSnapshot['memoryFiles'][number]

function isCapacityCategory(category: ContextCategory) {
  const name = category.name.toLowerCase()
  return category.isDeferred || name.includes('free') || name.includes('autocompact')
}

function ContextStackedBar({ categories, rawMaxTokens }: { categories: ContextCategory[]; rawMaxTokens: number }) {
  const activeCategories = categories.filter((category) => !isCapacityCategory(category) && category.tokens > 0)
  if (activeCategories.length === 0) return null

  return (
    <div className="overflow-hidden rounded-full bg-[var(--color-inspector-chip)]">
      <div className="flex h-2.5 w-full">
        {activeCategories.map((category) => (
          <div
            key={category.name}
            title={`${category.name}: ${formatNumber(category.tokens)} tokens`}
            style={{
              width: `${Math.max(0.5, (category.tokens / rawMaxTokens) * 100)}%`,
              backgroundColor: category.color,
            }}
          />
        ))}
      </div>
    </div>
  )
}

function CategoryBreakdown({ categories, rawMaxTokens, t }: { categories: ContextCategory[]; rawMaxTokens: number; t: Translate }) {
  const visibleCategories = categories.filter((category) => category.tokens > 0)
  if (visibleCategories.length === 0) {
    return <EmptyState title={t('slash.inspector.context.noCategoriesTitle')} body={t('slash.inspector.context.noCategoriesBody')} />
  }

  return (
    <div className="rounded-md border border-[var(--color-inspector-border)] bg-[var(--color-inspector-panel)] px-5 py-5 font-mono">
      <InspectorSectionTitle>{t('slash.inspector.context.categoryTitle')}</InspectorSectionTitle>
      <div className="grid gap-x-10 gap-y-5 sm:grid-cols-2">
        {visibleCategories.map((category) => {
          const percent = rawMaxTokens > 0 ? (category.tokens / rawMaxTokens) * 100 : 0
          const muted = isCapacityCategory(category)
          return (
            <div
              key={category.name}
              className="min-w-0"
            >
              <div className="grid grid-cols-[minmax(0,1fr)_auto] items-center gap-3">
                <div className="flex min-w-0 items-center gap-2">
                  <span className={`min-w-0 truncate text-[14px] font-semibold ${muted ? 'text-[var(--color-inspector-muted-strong)]' : 'text-[var(--color-inspector-text)]'}`}>
                    {category.name}
                  </span>
                </div>
                <div className="shrink-0 text-right leading-tight">
                  <div className="text-sm text-[var(--color-inspector-text)]">{formatNumber(category.tokens)}</div>
                  <div className="mt-0.5 text-[12px] text-[var(--color-inspector-muted)]">{formatPercent(percent)}</div>
                </div>
              </div>
              <div className="mt-2 h-1 overflow-hidden rounded-full bg-[var(--color-inspector-chip)]">
                <div
                  className={muted ? 'h-full rounded-full opacity-65' : 'h-full rounded-full'}
                  style={{
                    width: `${Math.min(100, Math.max(0.5, percent))}%`,
                    backgroundColor: muted ? 'var(--color-inspector-capacity)' : 'var(--color-inspector-accent)',
                  }}
                />
              </div>
            </div>
          )
        })}
      </div>
    </div>
  )
}

function memoryContextFileLabel(path: string) {
  const normalized = path.replace(/\\/g, '/')
  return normalized.split('/').pop() || normalized
}

function MemoryFilesBreakdown({ files, t }: { files: ContextMemoryFile[]; t: Translate }) {
  const setPendingSettingsTab = useUIStore((state) => state.setPendingSettingsTab)
  const setPendingMemoryPath = useUIStore((state) => state.setPendingMemoryPath)
  const openSettings = (path?: string) => {
    if (path) setPendingMemoryPath(path)
    setPendingSettingsTab('memory')
    useTabStore.getState().openTab(SETTINGS_TAB_ID, 'Settings', 'settings')
  }

  if (files.length === 0) {
    return (
      <div className="rounded-md border border-[var(--color-inspector-border)] bg-[var(--color-inspector-panel)] px-5 py-5">
        <div className="flex items-center justify-between gap-3">
          <InspectorSectionTitle>{t('slash.inspector.context.memoryFiles')}</InspectorSectionTitle>
          <button
            type="button"
            onClick={() => openSettings()}
            className="rounded-sm border border-[var(--color-inspector-border)] bg-[var(--color-inspector-chip)] px-2.5 py-1 text-xs font-semibold text-[var(--color-inspector-muted-strong)] hover:text-[var(--color-inspector-text)]"
          >
            {t('slash.inspector.context.openMemory')}
          </button>
        </div>
        <div className="mt-4 text-sm text-[var(--color-inspector-muted)]">{t('slash.inspector.context.noMemoryFiles')}</div>
      </div>
    )
  }

  return (
    <div className="rounded-md border border-[var(--color-inspector-border)] bg-[var(--color-inspector-panel)] px-5 py-5">
      <div className="flex items-center justify-between gap-3">
        <InspectorSectionTitle>{t('slash.inspector.context.memoryFiles')}</InspectorSectionTitle>
        <button
          type="button"
          onClick={() => openSettings(files[0]?.path)}
          className="rounded-sm border border-[var(--color-inspector-border)] bg-[var(--color-inspector-chip)] px-2.5 py-1 text-xs font-semibold text-[var(--color-inspector-muted-strong)] hover:text-[var(--color-inspector-text)]"
        >
          {t('slash.inspector.context.openMemory')}
        </button>
      </div>
      <div className="mt-4 grid gap-2">
        {files.map((file) => (
          <div
            key={`${file.type}:${file.path}`}
            className="grid grid-cols-[minmax(0,1fr)_auto] items-center gap-3 rounded-md border border-[var(--color-inspector-border)] bg-[var(--color-inspector-surface)] px-3 py-2"
          >
            <div className="min-w-0">
              <div className="truncate font-mono text-sm font-semibold text-[var(--color-inspector-text)]" title={file.path}>
                {memoryContextFileLabel(file.path)}
              </div>
              <div className="mt-0.5 truncate font-mono text-[11px] text-[var(--color-inspector-muted)]" title={file.path}>
                {file.path}
              </div>
            </div>
            <div className="shrink-0 text-right font-mono">
              <div className="text-xs font-semibold uppercase tracking-[0.12em] text-[var(--color-inspector-muted-strong)]">{file.type}</div>
              <div className="mt-0.5 text-[11px] text-[var(--color-inspector-muted)]">{formatNumber(file.tokens)} tokens</div>
            </div>
          </div>
        ))}
      </div>
    </div>
  )
}

function ContextStatPill({ label, value, detail }: { label: string; value: string; detail?: string }) {
  return (
    <div className="min-w-0 font-mono">
      <div className="truncate text-[12px] font-semibold uppercase tracking-[0.22em] text-[var(--color-inspector-muted)]">{label}</div>
      <div className="mt-2 truncate text-[16px] font-semibold text-[var(--color-inspector-text)]">{value}</div>
      {detail && <div className="mt-1 truncate text-[13px] text-[var(--color-inspector-muted)]">{detail}</div>}
    </div>
  )
}

function statusDisplayLabel(status: string, t: Translate) {
  const normalized = status.toLowerCase()
  if (normalized === 'connected') return t('slash.inspector.status.connected')
  if (normalized === 'failed') return t('slash.inspector.status.failed')
  return status
}

function InspectorStatusBadge({ status, t }: { status: string; t: Translate }) {
  const normalized = status.toLowerCase()
  const isConnected = normalized === 'connected'
  const isFailed = normalized === 'failed'
  const badgeClass = isConnected
    ? 'bg-[var(--color-inspector-success-bg)] text-[var(--color-inspector-success)]'
    : isFailed
      ? 'bg-[var(--color-inspector-danger-bg)] text-[var(--color-inspector-danger)]'
      : 'bg-[var(--color-inspector-chip)] text-[var(--color-inspector-muted-strong)]'
  const dotClass = isConnected ? 'bg-[var(--color-inspector-success)]' : isFailed ? 'bg-[var(--color-inspector-danger)]' : 'bg-[var(--color-inspector-muted)]'

  return (
    <span className={`inline-flex items-center gap-1.5 rounded-sm px-2.5 py-1 font-mono text-[10px] font-semibold uppercase tracking-[0.08em] ${badgeClass}`}>
      <span className={`h-1.5 w-1.5 rounded-full ${dotClass}`} />
      {statusDisplayLabel(status, t)}
    </span>
  )
}

function McpServerIcon({ status }: { status: string }) {
  const isFailed = status === 'failed'
  const icon = isFailed ? 'power_off' : 'dns'
  return (
    <span className={`material-symbols-outlined text-[20px] ${isFailed ? 'text-[var(--color-inspector-danger)]' : 'text-[var(--color-inspector-success)]'}`}>
      {icon}
    </span>
  )
}

function ContextOverview({ context, categories, t }: { context: SessionContextSnapshot; categories: ContextCategory[]; t: Translate }) {
  const usedPercent = Math.min(100, Math.max(0, context.percentage))
  const freeTokens = Math.max(0, context.rawMaxTokens - context.totalTokens)
  const freePercent = context.rawMaxTokens > 0 ? (freeTokens / context.rawMaxTokens) * 100 : 0
  return (
    <div className="rounded-md border border-[var(--color-inspector-border)] bg-[var(--color-inspector-panel)] px-5 py-6">
      <div className="mb-8 flex items-start justify-between gap-4">
        <InspectorSectionTitle>{t('slash.inspector.context.windowUsage')}</InspectorSectionTitle>
        <span className="rounded-sm border border-[var(--color-inspector-border)] bg-[var(--color-inspector-chip)] px-2 py-1 font-mono text-xs text-[var(--color-inspector-muted-strong)]">{context.model}</span>
      </div>
      <div className="font-mono text-[24px] font-semibold text-[var(--color-inspector-text)]">
        {formatNumber(context.totalTokens)}
        <span className="mx-1.5 text-[var(--color-inspector-text)]">/</span>
        <span>{formatNumber(context.rawMaxTokens)}</span>
        <span className="ml-3 align-middle text-sm font-normal text-[var(--color-inspector-accent-secondary)]">[{formatPercent(usedPercent)} {t('slash.inspector.context.used')}]</span>
      </div>
      <div className="mt-7">
        <ContextStackedBar categories={categories} rawMaxTokens={context.rawMaxTokens} />
      </div>
      <div className="mt-8 grid grid-cols-2 gap-3 lg:grid-cols-4">
        <div className="rounded-md border border-[var(--color-inspector-border)] bg-[var(--color-inspector-surface)] px-4 py-3">
          <ContextStatPill label={t('slash.inspector.context.free')} value={formatNumber(freeTokens)} detail={formatPercent(freePercent)} />
        </div>
        <div className="rounded-md border border-[var(--color-inspector-border)] bg-[var(--color-inspector-surface)] px-4 py-3">
          <ContextStatPill label={t('slash.inspector.context.messages')} value={formatNumber(context.messageBreakdown?.assistantMessageTokens ?? 0)} detail={t('slash.inspector.context.assistant')} />
        </div>
        <div className="rounded-md border border-[var(--color-inspector-border)] bg-[var(--color-inspector-surface)] px-4 py-3">
          <ContextStatPill label={t('slash.inspector.context.toolResults')} value={formatNumber(context.messageBreakdown?.toolResultTokens ?? 0)} />
        </div>
        <div className="rounded-md border border-[var(--color-inspector-border)] bg-[var(--color-inspector-surface)] px-4 py-3">
          <ContextStatPill label={t('slash.inspector.context.context')} value={formatPercent(usedPercent)} />
        </div>
      </div>
    </div>
  )
}

function ContextTab({
  context,
  error,
  loading,
  t,
}: {
  context?: SessionContextSnapshot
  error?: string
  loading?: boolean
  t: Translate
}) {
  if (error && !context) return <ErrorState message={error} />
  if (loading && !context) return <LoadingState label={t('slash.inspector.context.loading')} />
  if (!context) {
    return <EmptyState title={t('slash.inspector.context.emptyTitle')} body={t('slash.inspector.context.emptyBody')} />
  }

  const categories = Array.isArray(context.categories) ? context.categories : []
  return (
    <div className="space-y-6">
      <ContextOverview context={context} categories={categories} t={t} />
      <MemoryFilesBreakdown files={Array.isArray(context.memoryFiles) ? context.memoryFiles : []} t={t} />
      <CategoryBreakdown categories={categories} rawMaxTokens={context.rawMaxTokens} t={t} />
    </div>
  )
}

function StatusTab({
  data,
  commands,
  t,
}: {
  data: SessionInspectionResponse
  commands?: SlashCommandOption[]
  t: Translate
}) {
  const mcpServers = Array.isArray(data.status.mcpServers) ? data.status.mcpServers : []
  const tools = Array.isArray(data.status.tools) ? data.status.tools : []
  const context = data.context ?? data.contextEstimate
  const model = data.status.model ?? context?.model ?? data.usage?.models?.[0]?.displayName ?? data.usage?.models?.[0]?.model ?? t('slash.inspector.status.unknown')
  const contextWindow = context?.rawMaxTokens ?? data.usage?.models?.find((entry) => entry.contextWindow > 0)?.contextWindow
  const slashCommandCount = (data.status.slashCommandCount ?? 0) > 0
    ? data.status.slashCommandCount
    : commands?.length ?? 0
  const connectedMcp = mcpServers.filter((server) => server.status === 'connected').length
  const failedMcp = mcpServers.filter((server) => server.status === 'failed').length
  return (
    <div className="space-y-8">
      <div className="grid grid-cols-2 gap-4 lg:grid-cols-4">
        <MetricCard
          label={t('slash.inspector.status.cliStatus')}
          value={(
            <span className="inline-flex items-center gap-2">
              <span className={`h-2 w-2 rounded-full ${data.active ? 'bg-[var(--color-inspector-success)]' : 'bg-[var(--color-inspector-danger)]'}`} />
              {data.active ? t('slash.inspector.status.running') : t('slash.inspector.status.notRunning')}
            </span>
          )}
        />
        <MetricCard
          label={t('slash.inspector.status.activeModel')}
          value={model}
          detail={contextWindow ? `${t('slash.inspector.status.contextWindow')}: ${formatNumber(contextWindow)}` : undefined}
        />
        <MetricCard
          label={t('slash.inspector.status.mcpConnections')}
          value={(
            <span>
              <span className="text-[var(--color-inspector-success)]">{formatNumber(connectedMcp)}</span>
              <span className="mx-5 text-[var(--color-inspector-text)]">/</span>
              <span className="text-[var(--color-inspector-danger)]">{formatNumber(failedMcp)}</span>
            </span>
          )}
          detail={(
            <span>
              <span className="text-[var(--color-inspector-success)]">{t('slash.inspector.status.connected')}</span>
              <span className="mx-5 text-[var(--color-inspector-text)]" />
              <span className="text-[var(--color-inspector-danger)]">{t('slash.inspector.status.failed')}</span>
            </span>
          )}
        />
        <MetricCard label={t('slash.inspector.status.registeredTools')} value={`${formatNumber(tools.length)} / ${formatNumber(slashCommandCount)} ${t('slash.inspector.status.commands')}`} />
      </div>
      <section>
        <InspectorSectionTitle>{t('slash.inspector.status.sessionMetadata')}</InspectorSectionTitle>
        <KeyValueRows
          rows={[
            [t('slash.inspector.status.version'), data.status.version ?? t('slash.inspector.status.unknown')],
            [t('slash.inspector.status.sessionId'), <span className="font-mono text-[13px]">{data.status.sessionId}</span>],
            [t('slash.inspector.status.workingDirectory'), <span className="font-mono text-[13px]">{data.status.cwd ?? data.status.workDir}</span>],
            [t('slash.inspector.status.permissionMode'), <span className="rounded-sm bg-[var(--color-inspector-chip)] px-1.5 py-1">{data.status.permissionMode}</span>],
            [t('slash.inspector.status.authToken'), data.status.apiKeySource ?? t('slash.inspector.status.unknown')],
            [t('slash.inspector.status.outputStyle'), data.status.outputStyle ?? t('slash.inspector.status.default')],
          ]}
        />
      </section>
      {mcpServers.length > 0 && (
        <section>
          <InspectorSectionTitle
            action={<button type="button" className="font-mono text-[12px] tracking-[0.18em] text-[var(--color-inspector-accent)] hover:text-[var(--color-inspector-accent-hover)]">↻ {t('slash.inspector.status.refresh')}</button>}
          >
            {t('slash.inspector.status.mcpServers')}
          </InspectorSectionTitle>
          <div className="grid gap-3 lg:grid-cols-2">
            {mcpServers.map((server) => (
              <div
                key={`${server.name}:${server.status}`}
                className="flex min-h-[48px] items-center justify-between gap-4 rounded-md border border-[var(--color-inspector-border)] bg-[var(--color-inspector-panel)] px-4 py-3 font-mono"
              >
                <div className="flex min-w-0 items-center gap-3">
                  <McpServerIcon status={server.status} />
                  <span className="min-w-0 truncate text-[14px] text-[var(--color-inspector-text)]">{server.name}</span>
                </div>
                <InspectorStatusBadge status={server.status} t={t} />
              </div>
            ))}
          </div>
        </section>
      )}
    </div>
  )
}

function SessionInspectorShell({
  selectedTab,
  tabs,
  onSelectTab,
  onClose,
  children,
  t,
}: {
  selectedTab: SessionInspectorTab
  tabs: Array<{ id: SessionInspectorTab; label: string }>
  onSelectTab: (tab: SessionInspectorTab) => void
  onClose: () => void
  children: React.ReactNode
  t: Translate
}) {
  return (
    <div
      className="absolute bottom-full left-0 right-0 z-50 mb-4 overflow-hidden rounded-[10px] border border-[var(--color-inspector-border)] bg-[var(--color-inspector-surface)] text-[var(--color-inspector-text)] shadow-[var(--shadow-inspector)]"
    >
      <div className="grid min-h-[64px] grid-cols-[1fr_auto_1fr] items-center border-b border-[var(--color-inspector-border)] bg-[var(--color-inspector-surface)] px-6">
        <div className="font-mono text-[16px] font-semibold uppercase text-[var(--color-inspector-accent)]">{t('slash.inspector.title')}</div>
        <div className="flex items-center gap-8">
          {tabs.map((tab) => (
            <button
              key={tab.id}
              type="button"
              onClick={() => onSelectTab(tab.id)}
              className={`relative h-10 px-0 font-sans text-sm transition-colors ${
                selectedTab === tab.id ? 'text-[var(--color-inspector-accent)]' : 'text-[var(--color-inspector-muted-strong)] hover:text-[var(--color-inspector-accent)]'
              }`}
            >
              {tab.label}
              {selectedTab === tab.id && <span className="absolute bottom-1 left-0 right-0 h-[2px] bg-[var(--color-inspector-accent)]" />}
            </button>
          ))}
        </div>
        <div className="flex justify-end">
          <button
            type="button"
            onClick={onClose}
            aria-label={t('slash.inspector.close')}
            className="flex h-10 w-10 items-center justify-center text-[var(--color-inspector-accent)] transition-colors hover:text-[var(--color-inspector-accent-hover)]"
          >
            <span className="material-symbols-outlined text-[24px]">close</span>
          </button>
        </div>
      </div>
      <div className="max-h-[min(540px,58vh)] overflow-y-auto bg-[var(--color-inspector-surface)] px-6 py-6">{children}</div>
    </div>
  )
}

function SessionInspectorPanel({
  command,
  sessionId,
  commands,
  onClose,
}: {
  command: LocalSlashCommandName
  sessionId?: string
  commands?: SlashCommandOption[]
  onClose: () => void
}) {
  const t = useTranslation()
  const [selectedTab, setSelectedTab] = useState<SessionInspectorTab>(() => sessionInspectorInitialTab(command))
  const [data, setData] = useState<SessionInspectionResponse | null>(null)
  const [error, setError] = useState<string | null>(null)
  const [contextLoading, setContextLoading] = useState(false)
  const [contextError, setContextError] = useState<string | null>(null)
  const contextRequestSessionRef = useRef<string | null>(null)

  useEffect(() => {
    if (command !== 'status' && command !== 'cost' && command !== 'context') return
    setSelectedTab(sessionInspectorInitialTab(command))
  }, [command])

  useEffect(() => {
    if (!sessionId) {
      setError(t('slash.inspector.error.noActiveSession'))
      return
    }
    let cancelled = false
    setData(null)
    setError(null)
    setContextLoading(false)
    setContextError(null)
    contextRequestSessionRef.current = null
    sessionsApi.getInspection(sessionId, { includeContext: false })
      .then((response) => {
        if (!cancelled) setData(assertSessionInspectionResponse(response, t))
      })
      .catch((err) => {
        if (!cancelled) setError(err instanceof Error ? err.message : String(err))
      })
    return () => {
      cancelled = true
    }
  }, [sessionId, t])

  useEffect(() => {
    if (!sessionId || selectedTab !== 'context' || data === null || data.context) return
    if (contextRequestSessionRef.current === sessionId) return
    contextRequestSessionRef.current = sessionId
    let cancelled = false
    setContextLoading(true)
    setContextError(null)
    sessionsApi.getInspection(sessionId, { includeContext: true, contextOnly: true, timeout: 45_000 })
      .then((response) => {
        if (cancelled) return
        const inspected = assertSessionInspectionResponse(response, t)
        setData((current) => current
          ? {
              ...current,
              context: inspected.context,
              errors: {
                ...(current.errors ?? {}),
                ...(inspected.errors ?? {}),
              },
            }
          : inspected)
        setContextError(inspected.errors?.context ?? null)
      })
      .catch((err) => {
        if (!cancelled) setContextError(err instanceof Error ? err.message : String(err))
      })
      .finally(() => {
        if (!cancelled) setContextLoading(false)
      })
    return () => {
      cancelled = true
    }
  }, [data, selectedTab, sessionId, t])

  const tabs: Array<{ id: SessionInspectorTab; label: string }> = [
    { id: 'status', label: t('slash.inspector.tab.status') },
    { id: 'usage', label: t('slash.inspector.tab.usage') },
    { id: 'context', label: t('slash.inspector.tab.context') },
  ]

  return (
    <SessionInspectorShell selectedTab={selectedTab} tabs={tabs} onSelectTab={setSelectedTab} onClose={onClose} t={t}>
      {error ? (
        <ErrorState message={error} />
      ) : data === null ? (
        <LoadingState label={t('slash.inspector.loading')} />
      ) : selectedTab === 'usage' ? (
        <UsageTab usage={data.usage} context={data.context ?? data.contextEstimate} error={data.errors?.usage} t={t} />
      ) : selectedTab === 'context' ? (
        <ContextTab
          context={data.context ?? data.contextEstimate}
          error={contextError ?? data.errors?.context}
          loading={contextLoading && !data.contextEstimate}
          t={t}
        />
      ) : (
        <StatusTab data={data} commands={commands} t={t} />
      )}
    </SessionInspectorShell>
  )
}

function McpPanel({ cwd, onClose }: { cwd?: string; onClose: () => void }) {
  const t = useTranslation()
  const setPendingSettingsTab = useUIStore((s) => s.setPendingSettingsTab)
  const selectServer = useMcpStore((s) => s.selectServer)
  const [servers, setServers] = useState<McpServerRecord[] | null>(null)
  const [error, setError] = useState<string | null>(null)

  useEffect(() => {
    let cancelled = false
    mcpApi.list(cwd)
      .then(async (response) => {
        if (cancelled) return
        const visibleServers = response.servers.filter((server) => server.scope === 'user' || server.scope === 'local' || server.scope === 'project')
        setServers(visibleServers)

        const statusResults = await Promise.allSettled(
          visibleServers.map((server) => mcpApi.status(server.name, cwd)),
        )
        if (cancelled) return

        const liveServers = new Map<string, McpServerRecord>()
        for (const result of statusResults) {
          if (result.status === 'fulfilled') {
            liveServers.set(result.value.server.name, result.value.server)
          }
        }
        if (liveServers.size > 0) {
          setServers((current) =>
            current?.map((server) => liveServers.get(server.name) ?? server) ?? current,
          )
        }
      })
      .catch((err) => {
        if (cancelled) return
        setError(err instanceof Error ? err.message : String(err))
      })
    return () => {
      cancelled = true
    }
  }, [cwd])

  const grouped = useMemo(() => {
    const groups = new Map<string, McpServerRecord[]>()
    for (const server of servers ?? []) {
      const key = server.scope
      const existing = groups.get(key) ?? []
      existing.push(server)
      groups.set(key, existing)
    }
    return groups
  }, [servers])

  return (
    <PanelShell
      title={t('slash.mcp.title')}
      subtitle={cwd ? t('slash.mcp.subtitleWithProject', { path: cwd }) : t('slash.mcp.subtitle')}
      onClose={onClose}
    >
      {error ? (
        <ErrorState message={error} />
      ) : servers === null ? (
        <LoadingState label={t('common.loading')} />
      ) : servers.length === 0 ? (
        <EmptyState title={t('slash.mcp.emptyTitle')} body={t('slash.mcp.emptyBody')} />
      ) : (
        <div className="space-y-5">
          {['user', 'local', 'project'].filter((scope) => grouped.has(scope)).map((scope) => (
            <section key={scope}>
              <div className="mb-2 flex items-center justify-between">
                <div className="text-sm font-semibold text-[var(--color-text-primary)]">{scopeLabel(scope, t)}</div>
                <div className="text-xs text-[var(--color-text-tertiary)]">{grouped.get(scope)?.length ?? 0}</div>
              </div>
              <div className="overflow-hidden rounded-2xl border border-[var(--color-border)] bg-[var(--color-surface)]">
                {grouped.get(scope)?.map((server) => (
                  <button
                    type="button"
                    key={`${server.scope}:${server.projectPath ?? 'global'}:${server.name}`}
                    onClick={() => {
                      selectServer(server)
                      setPendingSettingsTab('mcp')
                      useTabStore.getState().openTab(SETTINGS_TAB_ID, 'Settings', 'settings')
                      onClose()
                    }}
                    className="block w-full border-t border-[var(--color-border)] px-4 py-4 text-left first:border-t-0 hover:bg-[var(--color-surface-hover)]"
                  >
                    <div className="flex items-center gap-3">
                      <div className="text-sm font-semibold text-[var(--color-text-primary)]">{server.name}</div>
                      <span className={`inline-flex items-center rounded-full border px-2 py-1 text-[11px] font-semibold ${toneForStatus(server.status)}`}>
                        {server.statusLabel}
                      </span>
                    </div>
                    <div className="mt-2 flex flex-wrap items-center gap-2 text-xs text-[var(--color-text-tertiary)]">
                      <span className="rounded-full bg-[var(--color-surface-hover)] px-2 py-1">{server.transport}</span>
                      {server.projectPath && (
                        <span className="rounded-full bg-[var(--color-surface-hover)] px-2 py-1" title={server.projectPath}>
                          {projectBadge(server.projectPath, t)}
                        </span>
                      )}
                      <span className="truncate">{server.summary}</span>
                    </div>
                  </button>
                ))}
              </div>
            </section>
          ))}
        </div>
      )}
    </PanelShell>
  )
}

function SkillsPanel({ cwd, onClose }: { cwd?: string; onClose: () => void }) {
  const t = useTranslation()
  const setPendingSettingsTab = useUIStore((s) => s.setPendingSettingsTab)
  const fetchSkillDetail = useSkillStore((s) => s.fetchSkillDetail)
  const [skills, setSkills] = useState<SkillMeta[] | null>(null)
  const [error, setError] = useState<string | null>(null)

  useEffect(() => {
    let cancelled = false
    skillsApi.list(cwd)
      .then((response) => {
        if (cancelled) return
        setSkills(response.skills.filter((skill) => skill.userInvocable))
      })
      .catch((err) => {
        if (cancelled) return
        setError(err instanceof Error ? err.message : String(err))
      })
    return () => {
      cancelled = true
    }
  }, [cwd])

  return (
    <PanelShell
      title={t('slash.skills.title')}
      subtitle={cwd ? t('slash.skills.subtitleWithProject', { path: cwd }) : t('slash.skills.subtitle')}
      onClose={onClose}
    >
      {error ? (
        <ErrorState message={error} />
      ) : skills === null ? (
        <LoadingState label={t('common.loading')} />
      ) : skills.length === 0 ? (
        <EmptyState title={t('slash.skills.emptyTitle')} body={t('slash.skills.emptyBody')} />
      ) : (
        <div className="overflow-hidden rounded-2xl border border-[var(--color-border)] bg-[var(--color-surface)]">
          {skills.map((skill) => (
            <button
              type="button"
              key={`${skill.source}:${skill.name}`}
              onClick={async () => {
                await fetchSkillDetail(skill.source, skill.name, cwd, 'skills')
                setPendingSettingsTab('skills')
                useTabStore.getState().openTab(SETTINGS_TAB_ID, 'Settings', 'settings')
                onClose()
              }}
              className="block w-full border-t border-[var(--color-border)] px-4 py-4 text-left first:border-t-0 hover:bg-[var(--color-surface-hover)]"
            >
              <div className="flex items-center gap-3">
                <div className="text-sm font-semibold text-[var(--color-text-primary)]">/{skill.name}</div>
                <span className="rounded-full bg-[var(--color-surface-hover)] px-2 py-1 text-[11px] text-[var(--color-text-secondary)]">
                  {skill.source}
                </span>
              </div>
              <div className="mt-2 text-xs leading-6 text-[var(--color-text-tertiary)]">{skill.description}</div>
            </button>
          ))}
        </div>
      )}
    </PanelShell>
  )
}

const COMMAND_GROUPS = [
  {
    titleKey: 'slash.help.group.context',
    names: ['clear', 'compact', 'context', 'cost'],
  },
  {
    titleKey: 'slash.help.group.project',
    names: ['init', 'review', 'commit', 'pr'],
  },
  {
    titleKey: 'slash.help.group.desktop',
    names: ['mcp', 'skills', 'plugin', 'help'],
  },
] satisfies Array<{ titleKey: TranslationKey; names: string[] }>

function HelpPanel({
  commands,
  onClose,
}: {
  commands?: SlashCommandOption[]
  onClose: () => void
}) {
  const t = useTranslation()
  const commandMap = useMemo(() => {
    const map = new Map<string, SlashCommandOption>()
    for (const command of commands ?? []) {
      map.set(command.name, command)
    }
    return map
  }, [commands])

  const groupedNames = new Set(COMMAND_GROUPS.flatMap((group) => group.names))
  const otherCommands = (commands ?? [])
    .filter((command) => !groupedNames.has(command.name))
    .slice(0, 12)
  const hiddenOtherCommandCount = Math.max(
    0,
    (commands ?? []).filter((command) => !groupedNames.has(command.name)).length - otherCommands.length,
  )

  const renderCommand = (command: SlashCommandOption) => (
    <div key={command.name} className="flex min-w-0 items-start gap-3 border-t border-[var(--color-border)] px-4 py-3 first:border-t-0">
      <div className="flex min-w-[120px] max-w-[45%] shrink-0 flex-wrap items-baseline gap-x-1.5 font-mono">
        <span className="text-sm font-semibold text-[var(--color-text-primary)]">/{command.name}</span>
        {command.argumentHint ? (
          <span className="text-[11px] leading-5 text-[var(--color-text-tertiary)]">{command.argumentHint}</span>
        ) : null}
      </div>
      <div className="min-w-0 flex-1 text-xs leading-5 text-[var(--color-text-tertiary)]">{command.description}</div>
    </div>
  )

  return (
    <PanelShell
      title={t('slash.help.title')}
      subtitle={t('slash.help.subtitle')}
      onClose={onClose}
    >
      <div className="space-y-4">
        {COMMAND_GROUPS.map((group) => {
          const entries = group.names
            .map((name) => commandMap.get(name))
            .filter((command): command is SlashCommandOption => Boolean(command))
          if (entries.length === 0) return null
          return (
            <section key={group.titleKey}>
              <div className="mb-2 text-sm font-semibold text-[var(--color-text-primary)]">{t(group.titleKey)}</div>
              <div className="overflow-hidden rounded-2xl border border-[var(--color-border)] bg-[var(--color-surface)]">
                {entries.map(renderCommand)}
              </div>
            </section>
          )
        })}

        {otherCommands.length > 0 && (
          <section>
            <div className="mb-2 text-sm font-semibold text-[var(--color-text-primary)]">{t('slash.help.group.more')}</div>
            <div className="overflow-hidden rounded-2xl border border-[var(--color-border)] bg-[var(--color-surface)]">
              {otherCommands.map(renderCommand)}
            </div>
            {hiddenOtherCommandCount > 0 && (
              <p className="mt-2 text-xs leading-5 text-[var(--color-text-tertiary)]">
                {t('slash.help.moreAvailable', { count: hiddenOtherCommandCount })}
              </p>
            )}
          </section>
        )}
      </div>
    </PanelShell>
  )
}

export function LocalSlashCommandPanel({ command, sessionId, cwd, commands, onClose }: Props) {
  if (command === 'mcp') return <McpPanel cwd={cwd} onClose={onClose} />
  if (command === 'skills') return <SkillsPanel cwd={cwd} onClose={onClose} />
  if (command === 'status' || command === 'cost' || command === 'context') {
    return <SessionInspectorPanel command={command} sessionId={sessionId} commands={commands} onClose={onClose} />
  }
  return <HelpPanel commands={commands} onClose={onClose} />
}
