import { useState, useMemo, useCallback } from 'react'
import { useParams, useNavigate, useSearchParams } from 'react-router'
import { AlertTriangle, X, Search } from 'lucide-react'
import {
  DataTable,
  ShortcutsBar,
  KpiStrip,
  StatusDot,
  MonoText,
  Badge,
  useGlobalFilters,
} from '@cameleer/design-system'
import type { Column, KpiItem } from '@cameleer/design-system'
import {
  useSearchExecutions,
  useExecutionStats,
  useStatsTimeseries,
} from '../../api/queries/executions'
import type { ExecutionSummary } from '../../api/types'
import styles from './Dashboard.module.css'

// Row type extends ExecutionSummary with an `id` field for DataTable
interface Row extends ExecutionSummary {
  id: string
}

// ─── Helpers ─────────────────────────────────────────────────────────────────

function formatDuration(ms: number): string {
  if (ms >= 60_000) return `${(ms / 1000).toFixed(0)}s`
  if (ms >= 1000) return `${(ms / 1000).toFixed(2)}s`
  return `${ms}ms`
}

function formatTimestamp(iso: string): string {
  const date = new Date(iso)
  const y = date.getFullYear()
  const mo = String(date.getMonth() + 1).padStart(2, '0')
  const d = String(date.getDate()).padStart(2, '0')
  const h = String(date.getHours()).padStart(2, '0')
  const mi = String(date.getMinutes()).padStart(2, '0')
  const s = String(date.getSeconds()).padStart(2, '0')
  return `${y}-${mo}-${d} ${h}:${mi}:${s}`
}

function statusToVariant(status: string): 'success' | 'error' | 'running' | 'warning' {
  switch (status) {
    case 'COMPLETED': return 'success'
    case 'FAILED': return 'error'
    case 'RUNNING': return 'running'
    default: return 'warning'
  }
}

function statusLabel(status: string): string {
  switch (status) {
    case 'COMPLETED': return 'OK'
    case 'FAILED': return 'ERR'
    case 'RUNNING': return 'RUN'
    default: return 'WARN'
  }
}

function durationClass(ms: number, status: string): string {
  if (status === 'FAILED') return styles.durBreach
  if (ms < 100) return styles.durFast
  if (ms < 200) return styles.durNormal
  if (ms < 300) return styles.durSlow
  return styles.durBreach
}

function flattenProcessors(nodes: any[]): any[] {
  const result: any[] = []
  let offset = 0
  function walk(node: any) {
    result.push({
      name: node.processorId || node.processorType,
      type: node.processorType,
      durationMs: node.durationMs ?? 0,
      status: node.status === 'COMPLETED' ? 'ok' : node.status === 'FAILED' ? 'fail' : 'ok',
      startMs: offset,
    })
    offset += node.durationMs ?? 0
    if (node.children) node.children.forEach(walk)
  }
  nodes.forEach(walk)
  return result
}

// ─── Table columns (base, without inspect action) ────────────────────────────

function buildBaseColumns(): Column<Row>[] {
  return [
    {
      key: 'status',
      header: 'Status',
      width: '80px',
      render: (_: unknown, row: Row) => (
        <span className={styles.statusCell}>
          <StatusDot variant={statusToVariant(row.status)} />
          <MonoText size="xs">{statusLabel(row.status)}</MonoText>
        </span>
      ),
    },
    {
      key: 'routeId',
      header: 'Route',
      sortable: true,
      render: (_: unknown, row: Row) => (
        <span className={styles.routeName}>{row.routeId}</span>
      ),
    },
    {
      key: 'applicationName',
      header: 'Application',
      sortable: true,
      render: (_: unknown, row: Row) => (
        <span className={styles.appName}>{row.applicationName ?? ''}</span>
      ),
    },
    {
      key: 'attributes',
      header: 'Attributes',
      render: (_, row) => {
        const attrs = row.attributes;
        if (!attrs || Object.keys(attrs).length === 0) return <span className={styles.muted}>—</span>;
        const entries = Object.entries(attrs);
        const shown = entries.slice(0, 2);
        const overflow = entries.length - 2;
        return (
          <div className={styles.attrCell}>
            {shown.map(([k, v]) => (
              <span key={k} title={k}>
                <Badge label={String(v)} color="auto" />
              </span>
            ))}
            {overflow > 0 && <span className={styles.attrOverflow}>+{overflow}</span>}
          </div>
        );
      },
    },
    {
      key: 'executionId',
      header: 'Exchange ID',
      sortable: true,
      render: (_: unknown, row: Row) => (
        <MonoText size="xs">{row.executionId}</MonoText>
      ),
    },
    {
      key: 'startTime',
      header: 'Started',
      sortable: true,
      render: (_: unknown, row: Row) => (
        <MonoText size="xs">{formatTimestamp(row.startTime)}</MonoText>
      ),
    },
    {
      key: 'durationMs',
      header: 'Duration',
      sortable: true,
      render: (_: unknown, row: Row) => (
        <MonoText size="sm" className={durationClass(row.durationMs, row.status)}>
          {formatDuration(row.durationMs)}
        </MonoText>
      ),
    },
    {
      key: 'agentId',
      header: 'Agent',
      render: (_: unknown, row: Row) => (
        <span className={styles.agentBadge}>
          <span className={styles.agentDot} />
          {row.agentId}
        </span>
      ),
    },
  ]
}

const SHORTCUTS = [
  { keys: 'Ctrl+K', label: 'Search' },
  { keys: '\u2191\u2193', label: 'Navigate rows' },
  { keys: 'Enter', label: 'Open detail' },
  { keys: 'Esc', label: 'Close panel' },
]

// ─── Dashboard component ─────────────────────────────────────────────────────

export default function Dashboard() {
  const { appId, routeId } = useParams<{ appId: string; routeId: string }>()
  const navigate = useNavigate()
  const [searchParams, setSearchParams] = useSearchParams()
  const textFilter = searchParams.get('text') || undefined
  const [selectedId, setSelectedId] = useState<string | undefined>()
  const [sortField, setSortField] = useState<string>('startTime')
  const [sortDir, setSortDir] = useState<'asc' | 'desc'>('desc')

  const { timeRange, statusFilters } = useGlobalFilters()
  const timeFrom = timeRange.start.toISOString()
  const timeTo = timeRange.end.toISOString()
  const timeWindowSeconds = (timeRange.end.getTime() - timeRange.start.getTime()) / 1000

  const handleSortChange = useCallback((key: string, dir: 'asc' | 'desc') => {
    setSortField(key)
    setSortDir(dir)
  }, [])

  // ─── API hooks ───────────────────────────────────────────────────────────
  const { data: stats } = useExecutionStats(timeFrom, timeTo, routeId, appId)
  const { data: timeseries } = useStatsTimeseries(timeFrom, timeTo, routeId, appId)
  // Convert design-system status filters (lowercase) to API status param (uppercase)
  const statusParam = statusFilters.size > 0
    ? [...statusFilters].map(s => s.toUpperCase()).join(',')
    : undefined

  const { data: searchResult } = useSearchExecutions(
    {
      timeFrom,
      timeTo,
      routeId: routeId || undefined,
      application: appId || undefined,
      status: statusParam,
      text: textFilter,
      sortField,
      sortDir,
      offset: 0,
      limit: textFilter ? 200 : 50,
    },
    !textFilter,
  )

  // ─── Rows ────────────────────────────────────────────────────────────────
  const rows: Row[] = useMemo(
    () => (searchResult?.data || []).map((e: ExecutionSummary) => ({ ...e, id: e.executionId })),
    [searchResult],
  )

  // ─── KPI items ───────────────────────────────────────────────────────────
  const totalCount = stats?.totalCount ?? 0
  const failedCount = stats?.failedCount ?? 0
  const successRate = totalCount > 0 ? ((totalCount - failedCount) / totalCount) * 100 : 100
  const throughput = timeWindowSeconds > 0 ? totalCount / timeWindowSeconds : 0

  const prevTotal = stats?.prevTotalCount ?? 0
  const prevFailed = stats?.prevFailedCount ?? 0
  const exchangeTrend = prevTotal > 0 ? ((totalCount - prevTotal) / prevTotal) * 100 : 0
  const prevSuccessRate = prevTotal > 0 ? ((prevTotal - prevFailed) / prevTotal) * 100 : 100
  const successRateDelta = successRate - prevSuccessRate
  const errorDelta = failedCount - prevFailed

  const sparkExchanges = useMemo(
    () => (timeseries?.buckets || []).map((b: any) => b.totalCount as number),
    [timeseries],
  )
  const sparkErrors = useMemo(
    () => (timeseries?.buckets || []).map((b: any) => b.failedCount as number),
    [timeseries],
  )
  const sparkLatency = useMemo(
    () => (timeseries?.buckets || []).map((b: any) => b.p99DurationMs as number),
    [timeseries],
  )
  const sparkThroughput = useMemo(
    () =>
      (timeseries?.buckets || []).map((b: any) => {
        const bucketSeconds = timeWindowSeconds / Math.max((timeseries?.buckets || []).length, 1)
        return bucketSeconds > 0 ? (b.totalCount as number) / bucketSeconds : 0
      }),
    [timeseries, timeWindowSeconds],
  )

  const kpiItems: KpiItem[] = useMemo(
    () => [
      {
        label: 'Exchanges',
        value: totalCount.toLocaleString(),
        trend: {
          label: `${exchangeTrend > 0 ? '\u2191' : exchangeTrend < 0 ? '\u2193' : '\u2192'} ${exchangeTrend > 0 ? '+' : ''}${exchangeTrend.toFixed(0)}%`,
          variant: (exchangeTrend > 0 ? 'success' : exchangeTrend < 0 ? 'error' : 'muted') as 'success' | 'error' | 'muted',
        },
        subtitle: `${successRate.toFixed(1)}% success rate`,
        sparkline: sparkExchanges,
        borderColor: 'var(--amber)',
      },
      {
        label: 'Success Rate',
        value: `${successRate.toFixed(1)}%`,
        trend: {
          label: `${successRateDelta >= 0 ? '\u2191' : '\u2193'} ${successRateDelta >= 0 ? '+' : ''}${successRateDelta.toFixed(1)}%`,
          variant: (successRateDelta >= 0 ? 'success' : 'error') as 'success' | 'error',
        },
        subtitle: `${(totalCount - failedCount).toLocaleString()} ok / ${failedCount} error`,
        borderColor: 'var(--success)',
      },
      {
        label: 'Errors',
        value: failedCount,
        trend: {
          label: `${errorDelta > 0 ? '\u2191' : errorDelta < 0 ? '\u2193' : '\u2192'} ${errorDelta > 0 ? '+' : ''}${errorDelta}`,
          variant: (errorDelta > 0 ? 'error' : errorDelta < 0 ? 'success' : 'muted') as 'success' | 'error' | 'muted',
        },
        subtitle: `${failedCount} errors in selected period`,
        sparkline: sparkErrors,
        borderColor: 'var(--error)',
      },
      {
        label: 'Throughput',
        value: `${throughput.toFixed(1)} msg/s`,
        trend: { label: '\u2192', variant: 'muted' as const },
        subtitle: `${throughput.toFixed(1)} msg/s`,
        sparkline: sparkThroughput,
        borderColor: 'var(--running)',
      },
      {
        label: 'Latency p99',
        value: `${(stats?.p99LatencyMs ?? 0).toLocaleString()} ms`,
        trend: { label: '', variant: 'muted' as const },
        subtitle: `${(stats?.p99LatencyMs ?? 0).toLocaleString()}ms`,
        sparkline: sparkLatency,
        borderColor: 'var(--warning)',
      },
    ],
    [totalCount, failedCount, successRate, throughput, exchangeTrend, successRateDelta, errorDelta, sparkExchanges, sparkErrors, sparkLatency, sparkThroughput, stats?.p99LatencyMs],
  )

  // ─── Table columns ──────────────────────────────────────────────────────
  const columns: Column<Row>[] = useMemo(() => buildBaseColumns(), [])

  // ─── Row click → navigate to diagram view ────────────────────────────────

  function handleRowClick(row: Row) {
    setSelectedId(row.id)
    // Navigate to the split view with diagram
    navigate(`/exchanges/${row.applicationName}/${row.routeId}/${row.executionId}`)
  }

  function handleRowAccent(row: Row): 'error' | 'warning' | undefined {
    if (row.status === 'FAILED') return 'error'
    return undefined
  }

  return (
    <>
      {/* Scrollable content */}
      <div className={styles.content}>
        {/* KPI strip */}
        <KpiStrip items={kpiItems} />

        {/* Exchanges table */}
        <div className={styles.tableSection}>
          <div className={styles.tableHeader}>
            <span className={styles.tableTitle}>
              {textFilter ? (
                <>
                  <Search size={14} style={{ marginRight: 4, verticalAlign: -2 }} />
                  Search: &ldquo;{textFilter}&rdquo;
                  <button
                    className={styles.clearSearch}
                    onClick={() => setSearchParams({})}
                    title="Clear search"
                  >
                    <X size={12} />
                  </button>
                </>
              ) : 'Recent Exchanges'}
            </span>
            <div className={styles.tableRight}>
              <span className={styles.tableMeta}>
                {rows.length.toLocaleString()} of {(searchResult?.total ?? 0).toLocaleString()} exchanges
              </span>
              {!textFilter && <Badge label="LIVE" color="success" />}
            </div>
          </div>

          <DataTable
            columns={columns}
            data={rows}
            onRowClick={handleRowClick}
            selectedId={selectedId}
            sortable
            flush
            onSortChange={handleSortChange}
            rowAccent={handleRowAccent}
            expandedContent={(row: Row) =>
              row.errorMessage ? (
                <div className={styles.inlineError}>
                  <span className={styles.inlineErrorIcon}><AlertTriangle size={14} /></span>
                  <div>
                    <div className={styles.inlineErrorText}>{row.errorMessage}</div>
                    <div className={styles.inlineErrorHint}>Click to view full stack trace</div>
                  </div>
                </div>
              ) : null
            }
          />
        </div>
      </div>

      {/* Shortcuts bar */}
      <ShortcutsBar shortcuts={SHORTCUTS} />
    </>
  )
}
