/**
 * React UI for rendering devops info.
 *
 * Usage:
 * ```tsx
 * import { DevopsPanel } from '@bytelyst/devops/ui';
 *
 * <DevopsPanel fetchInfo={() => fetch('/api/devops/info').then(r => r.json())} />
 * ```
 *
 * Styling: uses plain HTML + inline styles (theme-aware via CSS variables).
 * No hard dependency on any UI framework — works in any React app.
 */

import * as React from 'react';
import type { DevopsInfo } from './types.js';

export type { DevopsInfo };

export interface DevopsPanelProps {
  /** Async loader for devops info — typically calls `/api/devops/info`. */
  fetchInfo: () => Promise<DevopsInfo>;
  /** Refresh interval in milliseconds. Default: 10_000 (10s). Set to 0 to disable. */
  refreshIntervalMs?: number;
  /** Optional second source (e.g. web bundle's own info). */
  fetchWebInfo?: () => Promise<DevopsInfo>;
}

type Tab = 'build' | 'runtime' | 'config' | 'dependencies' | 'raw';

export function DevopsPanel(props: DevopsPanelProps): React.ReactElement {
  const { fetchInfo, fetchWebInfo, refreshIntervalMs = 10_000 } = props;
  const [info, setInfo] = React.useState<DevopsInfo | null>(null);
  const [webInfo, setWebInfo] = React.useState<DevopsInfo | null>(null);
  const [error, setError] = React.useState<string | null>(null);
  const [loading, setLoading] = React.useState(true);
  const [tab, setTab] = React.useState<Tab>('build');
  const [source, setSource] = React.useState<'backend' | 'web'>('backend');
  const [lastRefreshed, setLastRefreshed] = React.useState<Date | null>(null);

  const load = React.useCallback(async () => {
    try {
      const [b, w] = await Promise.all([
        fetchInfo().catch(e => {
          throw e;
        }),
        fetchWebInfo ? fetchWebInfo().catch(() => null) : Promise.resolve(null),
      ]);
      setInfo(b);
      setWebInfo(w);
      setError(null);
      setLastRefreshed(new Date());
    } catch (e) {
      setError(e instanceof Error ? e.message : String(e));
    } finally {
      setLoading(false);
    }
  }, [fetchInfo, fetchWebInfo]);

  React.useEffect(() => {
    void load();
    if (refreshIntervalMs > 0) {
      const h = setInterval(() => void load(), refreshIntervalMs);
      return () => clearInterval(h);
    }
    return undefined;
  }, [load, refreshIntervalMs]);

  const active = source === 'web' && webInfo ? webInfo : info;

  if (loading && !active) {
    return <div style={styles.msg}>Loading devops info…</div>;
  }
  if (error && !active) {
    return (
      <div style={{ ...styles.msg, color: 'var(--destructive, #ef4444)' }}>Error: {error}</div>
    );
  }
  if (!active) {
    return <div style={styles.msg}>No devops info available.</div>;
  }

  const tabs: Array<{ id: Tab; label: string; hidden?: boolean }> = [
    { id: 'build', label: 'Build' },
    { id: 'runtime', label: 'Runtime' },
    { id: 'config', label: 'Config' },
    { id: 'dependencies', label: 'Dependencies', hidden: !active.dependencies?.length },
    { id: 'raw', label: 'Raw JSON' },
  ];

  return (
    <div style={styles.root}>
      <div style={styles.header}>
        <div>
          <div style={styles.title}>DevOps · {active.config.serviceName}</div>
          <div style={styles.subtitle}>
            {active.config.productId} · v{active.config.serviceVersion} ·{' '}
            {active.build.commitSha ? (
              <code style={styles.code}>{active.build.commitSha}</code>
            ) : (
              <span style={styles.muted}>no commit info</span>
            )}{' '}
            · <span style={styles.muted}>uptime {active.runtime.uptimeHuman}</span>
          </div>
        </div>
        <div style={styles.headerActions}>
          {webInfo ? (
            <div style={styles.sourceToggle}>
              {(['backend', 'web'] as const).map(s => (
                <button
                  key={s}
                  type="button"
                  onClick={() => setSource(s)}
                  style={{ ...styles.sourceBtn, ...(source === s ? styles.sourceBtnActive : {}) }}
                >
                  {s}
                </button>
              ))}
            </div>
          ) : null}
          <button
            type="button"
            onClick={() => void load()}
            style={styles.refreshBtn}
            disabled={loading}
          >
            {loading ? 'Refreshing…' : 'Refresh'}
          </button>
        </div>
      </div>

      {lastRefreshed ? (
        <div style={styles.muted}>Last refreshed: {lastRefreshed.toLocaleTimeString()}</div>
      ) : null}
      {error ? (
        <div style={{ ...styles.msg, color: 'var(--destructive, #ef4444)' }}>
          Stale data (refresh failed): {error}
        </div>
      ) : null}

      <div style={styles.tabs} role="tablist">
        {tabs
          .filter(t => !t.hidden)
          .map(t => (
            <button
              key={t.id}
              type="button"
              role="tab"
              aria-selected={tab === t.id}
              onClick={() => setTab(t.id)}
              style={{ ...styles.tab, ...(tab === t.id ? styles.tabActive : {}) }}
            >
              {t.label}
            </button>
          ))}
      </div>

      <div style={styles.panel}>
        {tab === 'build' && <KvTable rows={buildRows(active)} />}
        {tab === 'runtime' && <KvTable rows={runtimeRows(active)} />}
        {tab === 'config' && <KvTable rows={configRows(active)} />}
        {tab === 'dependencies' && active.dependencies?.length ? (
          <DependenciesTable deps={active.dependencies} />
        ) : null}
        {tab === 'raw' && <pre style={styles.pre}>{JSON.stringify(active, null, 2)}</pre>}
      </div>
    </div>
  );
}

function buildRows(info: DevopsInfo): KvRow[] {
  const b = info.build;
  return [
    {
      key: 'Commit',
      value: b.commitSha ? <code style={styles.code}>{b.commitShaFull || b.commitSha}</code> : '—',
    },
    { key: 'Branch', value: b.branch ?? '—' },
    { key: 'Built at', value: b.builtAt ? formatTimestamp(b.builtAt) : '—' },
    { key: 'Author', value: b.commitAuthor ?? '—' },
    { key: 'Message', value: b.commitMessage ?? '—' },
    {
      key: 'Docker image',
      value: b.dockerImage ? <code style={styles.code}>{b.dockerImage}</code> : '—',
    },
  ];
}

function runtimeRows(info: DevopsInfo): KvRow[] {
  const r = info.runtime;
  return [
    { key: 'Uptime', value: r.uptimeHuman },
    { key: 'Started at', value: formatTimestamp(r.startedAt) },
    { key: 'Node version', value: r.nodeVersion },
    { key: 'Platform', value: `${r.platform} / ${r.arch}` },
    { key: 'Hostname', value: <code style={styles.code}>{r.hostname}</code> },
    { key: 'PID', value: String(r.pid) },
    { key: 'Memory (RSS)', value: `${r.memoryMb} MB` },
    { key: 'Heap used', value: `${r.heapMb} MB` },
  ];
}

function configRows(info: DevopsInfo): KvRow[] {
  const c = info.config;
  const rows: KvRow[] = [
    { key: 'Product', value: c.productId },
    { key: 'Service', value: c.serviceName },
    { key: 'Version', value: c.serviceVersion },
    { key: 'NODE_ENV', value: c.nodeEnv },
    {
      key: 'Env keys',
      value: c.envKeys.length ? <code style={styles.code}>{c.envKeys.join(', ')}</code> : '—',
    },
  ];
  if (info.extra) {
    for (const [k, v] of Object.entries(info.extra)) {
      rows.push({
        key: k,
        value:
          typeof v === 'object' ? <code style={styles.code}>{JSON.stringify(v)}</code> : String(v),
      });
    }
  }
  return rows;
}

interface KvRow {
  key: string;
  value: React.ReactNode;
}

function KvTable({ rows }: { rows: KvRow[] }): React.ReactElement {
  return (
    <div style={styles.tableWrap}>
      <table style={styles.table}>
        <tbody>
          {rows.map((r, i) => (
            <tr key={i} style={styles.tr}>
              <th style={styles.th} scope="row">
                {r.key}
              </th>
              <td style={styles.td}>{r.value}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

function DependenciesTable({
  deps,
}: {
  deps: NonNullable<DevopsInfo['dependencies']>;
}): React.ReactElement {
  return (
    <div style={styles.tableWrap}>
      <table style={styles.table}>
        <thead>
          <tr style={styles.tr}>
            <th style={{ ...styles.th, textAlign: 'left' }}>Name</th>
            <th style={{ ...styles.th, textAlign: 'left' }}>Status</th>
            <th style={{ ...styles.th, textAlign: 'left' }}>Latency</th>
            <th style={{ ...styles.th, textAlign: 'left' }}>Detail</th>
          </tr>
        </thead>
        <tbody>
          {deps.map((d, i) => (
            <tr key={i} style={styles.tr}>
              <td style={styles.td}>{d.name}</td>
              <td style={styles.td}>
                <span style={{ ...styles.badge, ...(d.ok ? styles.badgeOk : styles.badgeErr) }}>
                  {d.ok ? 'OK' : 'FAIL'}
                </span>
              </td>
              <td style={styles.td}>{d.latencyMs != null ? `${d.latencyMs} ms` : '—'}</td>
              <td style={styles.td}>{d.detail ?? '—'}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

function formatTimestamp(iso: string): string {
  try {
    const d = new Date(iso);
    return `${d.toLocaleString()} (${iso})`;
  } catch {
    return iso;
  }
}

const styles: Record<string, React.CSSProperties> = {
  root: { display: 'flex', flexDirection: 'column', gap: 12, maxWidth: '100%', minWidth: 0 },
  header: {
    display: 'flex',
    justifyContent: 'space-between',
    alignItems: 'flex-start',
    gap: 16,
    flexWrap: 'wrap',
  },
  title: { fontSize: 16, fontWeight: 600, color: 'var(--foreground, inherit)' },
  subtitle: { fontSize: 13, color: 'var(--muted-foreground, #6b7280)', marginTop: 4 },
  muted: { fontSize: 12, color: 'var(--muted-foreground, #6b7280)' },
  headerActions: { display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap' },
  sourceToggle: {
    display: 'inline-flex',
    border: '1px solid var(--border, #e5e7eb)',
    borderRadius: 6,
    overflow: 'hidden',
  },
  sourceBtn: {
    padding: '4px 10px',
    fontSize: 12,
    background: 'transparent',
    color: 'var(--muted-foreground, #6b7280)',
    border: 'none',
    cursor: 'pointer',
  },
  sourceBtnActive: {
    background: 'var(--accent, #2563eb)',
    color: 'var(--accent-foreground, #fff)',
  },
  refreshBtn: {
    padding: '4px 10px',
    fontSize: 12,
    background: 'transparent',
    border: '1px solid var(--border, #e5e7eb)',
    borderRadius: 6,
    cursor: 'pointer',
    color: 'var(--foreground, inherit)',
  },
  tabs: {
    display: 'flex',
    gap: 2,
    borderBottom: '1px solid var(--border, #e5e7eb)',
    overflowX: 'auto',
    whiteSpace: 'nowrap',
  },
  tab: {
    padding: '8px 14px',
    fontSize: 13,
    background: 'transparent',
    border: 'none',
    borderBottom: '2px solid transparent',
    cursor: 'pointer',
    color: 'var(--muted-foreground, #6b7280)',
    flexShrink: 0,
  },
  tabActive: {
    color: 'var(--foreground, inherit)',
    borderBottomColor: 'var(--accent, #2563eb)',
    fontWeight: 500,
  },
  panel: { paddingTop: 8, minWidth: 0 },
  tableWrap: {
    width: '100%',
    overflowX: 'auto',
    overflowY: 'visible',
    WebkitOverflowScrolling: 'touch',
  },
  table: { width: '100%', borderCollapse: 'collapse', fontSize: 13, tableLayout: 'fixed' },
  tr: { borderBottom: '1px solid var(--border, #f3f4f6)' },
  th: {
    textAlign: 'left',
    fontWeight: 500,
    color: 'var(--muted-foreground, #6b7280)',
    padding: '8px 12px 8px 0',
    verticalAlign: 'top',
    width: '30%',
    minWidth: 120,
  },
  td: {
    padding: '8px 0',
    verticalAlign: 'top',
    color: 'var(--foreground, inherit)',
    overflowWrap: 'anywhere',
    wordBreak: 'break-word',
  },
  code: {
    fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',
    fontSize: 12,
    background: 'color-mix(in oklab, var(--foreground, #000) 5%, transparent)',
    padding: '2px 6px',
    borderRadius: 4,
    wordBreak: 'break-all',
  },
  pre: {
    fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',
    fontSize: 12,
    background: 'color-mix(in oklab, var(--foreground, #000) 5%, transparent)',
    padding: 12,
    borderRadius: 6,
    overflow: 'auto',
    maxHeight: 480,
    whiteSpace: 'pre-wrap',
    wordBreak: 'break-word',
  },
  badge: {
    display: 'inline-block',
    padding: '2px 8px',
    borderRadius: 999,
    fontSize: 11,
    fontWeight: 500,
  },
  badgeOk: {
    background: 'color-mix(in oklab, #10b981 20%, transparent)',
    color: '#10b981',
  },
  badgeErr: {
    background: 'color-mix(in oklab, #ef4444 20%, transparent)',
    color: '#ef4444',
  },
  msg: {
    padding: 16,
    fontSize: 13,
    color: 'var(--muted-foreground, #6b7280)',
  },
};
