import { useEffect, useState, useMemo } from 'react';
import { useParams, useNavigate } from 'react-router';
import { ArrowLeft, Pencil, X } from 'lucide-react';
import {
  Button, SectionHeader, MonoText, Badge, DataTable, Spinner, Toggle, useToast,
} from '@cameleer/design-system';
import type { Column } from '@cameleer/design-system';
import { useApplicationConfig, useUpdateApplicationConfig } from '../../api/queries/commands';
import type { ApplicationConfig, TapDefinition, ConfigUpdateResponse } from '../../api/queries/commands';
import { useRouteCatalog } from '../../api/queries/catalog';
import type { AppCatalogEntry, RouteSummary } from '../../api/types';
import styles from './AppConfigDetailPage.module.css';

type BadgeColor = 'primary' | 'success' | 'warning' | 'error' | 'running' | 'auto';

interface TracedTapRow {
  id: string;
  processorId: string;
  captureMode: string | null;
  taps: TapDefinition[];
}

interface RouteRecordingRow {
  id: string;
  routeId: string;
  recording: boolean;
}

function formatTimestamp(iso?: string): string {
  if (!iso) return '\u2014';
  return new Date(iso).toLocaleString('en-GB', {
    day: '2-digit', month: 'short', year: 'numeric',
    hour: '2-digit', minute: '2-digit', second: '2-digit',
  });
}

function logLevelColor(level?: string): BadgeColor {
  switch (level?.toUpperCase()) {
    case 'ERROR': return 'error';
    case 'WARN': return 'warning';
    case 'DEBUG': return 'running';
    case 'TRACE': return 'auto';
    default: return 'success';
  }
}

function engineLevelColor(level?: string): BadgeColor {
  switch (level?.toUpperCase()) {
    case 'NONE': return 'error';
    case 'MINIMAL': return 'warning';
    case 'COMPLETE': return 'running';
    default: return 'success';
  }
}

function payloadColor(mode?: string): BadgeColor {
  switch (mode?.toUpperCase()) {
    case 'INPUT': case 'OUTPUT': return 'warning';
    case 'BOTH': return 'running';
    default: return 'auto';
  }
}

function captureColor(mode: string): BadgeColor {
  switch (mode?.toUpperCase()) {
    case 'INPUT': case 'OUTPUT': return 'warning';
    case 'BOTH': return 'running';
    default: return 'auto';
  }
}

export default function AppConfigDetailPage() {
  const { appId } = useParams<{ appId: string }>();
  const navigate = useNavigate();
  const { toast } = useToast();
  const { data: config, isLoading } = useApplicationConfig(appId);
  const updateConfig = useUpdateApplicationConfig();
  const { data: catalog } = useRouteCatalog();

  const [editing, setEditing] = useState(false);
  const [form, setForm] = useState<Partial<ApplicationConfig> | null>(null);
  const [tracedDraft, setTracedDraft] = useState<Record<string, string>>({});
  const [routeRecordingDraft, setRouteRecordingDraft] = useState<Record<string, boolean>>({});

  // Find routes for this application from the catalog
  const appRoutes: RouteSummary[] = useMemo(() => {
    if (!catalog || !appId) return [];
    const entry = (catalog as AppCatalogEntry[]).find((e) => e.appId === appId);
    return entry?.routes ?? [];
  }, [catalog, appId]);

  useEffect(() => {
    if (config) {
      setForm({
        applicationLogLevel: config.applicationLogLevel ?? 'INFO',
        agentLogLevel: config.agentLogLevel ?? 'INFO',
        engineLevel: config.engineLevel ?? 'REGULAR',
        payloadCaptureMode: config.payloadCaptureMode ?? 'NONE',
        metricsEnabled: config.metricsEnabled,
        samplingRate: config.samplingRate,
        compressSuccess: config.compressSuccess,
      });
      setTracedDraft({ ...config.tracedProcessors });
      setRouteRecordingDraft({ ...config.routeRecording });
    }
  }, [config]);

  function startEditing() {
    if (!config) return;
    setForm({
      applicationLogLevel: config.applicationLogLevel ?? 'INFO',
      agentLogLevel: config.agentLogLevel ?? 'INFO',
      engineLevel: config.engineLevel ?? 'REGULAR',
      payloadCaptureMode: config.payloadCaptureMode ?? 'NONE',
      metricsEnabled: config.metricsEnabled,
      samplingRate: config.samplingRate,
      compressSuccess: config.compressSuccess,
    });
    setTracedDraft({ ...config.tracedProcessors });
    setRouteRecordingDraft({ ...config.routeRecording });
    setEditing(true);
  }

  function cancelEditing() {
    setEditing(false);
  }

  function updateField<K extends keyof ApplicationConfig>(key: K, value: ApplicationConfig[K]) {
    setForm((prev) => prev ? { ...prev, [key]: value } : prev);
  }

  function updateTracedProcessor(processorId: string, mode: string) {
    setTracedDraft((prev) => {
      if (mode === 'REMOVE') {
        const next = { ...prev };
        delete next[processorId];
        return next;
      }
      return { ...prev, [processorId]: mode };
    });
  }

  function updateRouteRecording(routeId: string, recording: boolean) {
    setRouteRecordingDraft((prev) => ({ ...prev, [routeId]: recording }));
  }

  function handleSave() {
    if (!config || !form) return;
    const updated: ApplicationConfig = {
      ...config,
      ...form,
      tracedProcessors: tracedDraft,
      routeRecording: routeRecordingDraft,
    } as ApplicationConfig;
    updateConfig.mutate(updated, {
      onSuccess: (saved: ConfigUpdateResponse) => {
        setEditing(false);
        if (saved.pushResult.success) {
          toast({ title: 'Config saved', description: `${appId} updated to v${saved.config.version} — pushed to ${saved.pushResult.total}/${saved.pushResult.total} agents`, variant: 'success' });
        } else {
          const failed = [...saved.pushResult.responses.filter(r => r.status !== 'SUCCESS').map(r => r.agentId), ...saved.pushResult.timedOut];
          toast({ title: 'Config saved — partial push failure', description: `${saved.pushResult.responded}/${saved.pushResult.total} responded. Failed: ${failed.join(', ')}`, variant: 'warning', duration: 86_400_000 });
        }
      },
      onError: () => {
        toast({ title: 'Save failed', description: 'Could not update configuration', variant: 'error', duration: 86_400_000 });
      },
    });
  }

  // ── Traces & Taps merged rows ──────────────────────────────────────────────

  const tracedTapRows: TracedTapRow[] = useMemo(() => {
    const traced = editing ? tracedDraft : (config?.tracedProcessors ?? {});
    const taps = config?.taps ?? [];

    // Collect all unique processor IDs
    const processorIds = new Set<string>([
      ...Object.keys(traced),
      ...taps.map((t) => t.processorId),
    ]);

    return Array.from(processorIds).sort().map((pid) => ({
      id: pid,
      processorId: pid,
      captureMode: traced[pid] ?? null,
      taps: taps.filter((t) => t.processorId === pid),
    }));
  }, [editing, tracedDraft, config?.tracedProcessors, config?.taps]);

  const tracedCount = useMemo(() => {
    const source = editing ? tracedDraft : (config?.tracedProcessors ?? {});
    return Object.keys(source).length;
  }, [editing, tracedDraft, config?.tracedProcessors]);

  const tapCount = config?.taps?.length ?? 0;

  const tracedTapColumns: Column<TracedTapRow>[] = useMemo(() => [
    {
      key: 'processorId',
      header: 'Processor',
      render: (_v, row) => <MonoText size="xs">{row.processorId}</MonoText>,
    },
    {
      key: 'captureMode',
      header: 'Capture',
      render: (_v, row) => {
        if (row.captureMode === null) {
          return <span className={styles.hint}>&mdash;</span>;
        }
        if (editing) {
          return (
            <select
              className={styles.select}
              value={row.captureMode}
              onChange={(e) => updateTracedProcessor(row.processorId, e.target.value)}
            >
              <option value="NONE">None</option>
              <option value="INPUT">Input</option>
              <option value="OUTPUT">Output</option>
              <option value="BOTH">Both</option>
            </select>
          );
        }
        return <Badge label={row.captureMode} color={captureColor(row.captureMode)} variant="filled" />;
      },
    },
    {
      key: 'taps',
      header: 'Taps',
      render: (_v, row) => {
        if (row.taps.length === 0) {
          return <span className={styles.hint}>&mdash;</span>;
        }
        return (
          <div className={styles.tapBadges}>
            {row.taps.map((t) => (
              <Badge
                key={t.tapId}
                label={t.attributeName}
                color={t.enabled ? 'success' : 'auto'}
                variant="filled"
              />
            ))}
          </div>
        );
      },
    },
    ...(editing ? [{
      key: '_remove' as const,
      header: '',
      width: '36px',
      render: (_v: unknown, row: TracedTapRow) => {
        if (row.captureMode === null) return null;
        return (
          <button className={styles.removeBtn} title="Remove" onClick={() => updateTracedProcessor(row.processorId, 'REMOVE')}>
            <X size={14} />
          </button>
        );
      },
    }] : []),
  ], [editing]);

  // ── Route Recording rows ───────────────────────────────────────────────────

  const routeRecordingRows: RouteRecordingRow[] = useMemo(() => {
    const recording = editing ? routeRecordingDraft : (config?.routeRecording ?? {});
    return appRoutes.map((r) => ({
      id: r.routeId,
      routeId: r.routeId,
      recording: recording[r.routeId] !== false,
    }));
  }, [editing, routeRecordingDraft, config?.routeRecording, appRoutes]);

  const recordingCount = routeRecordingRows.filter((r) => r.recording).length;

  const routeRecordingColumns: Column<RouteRecordingRow>[] = useMemo(() => [
    {
      key: 'routeId',
      header: 'Route',
      render: (_v, row) => <MonoText size="xs">{row.routeId}</MonoText>,
    },
    {
      key: 'recording',
      header: 'Recording',
      width: '100px',
      render: (_v, row) => (
        <Toggle
          checked={row.recording}
          onChange={() => {
            if (editing) updateRouteRecording(row.routeId, !row.recording);
          }}
          disabled={!editing}
        />
      ),
    },
  ], [editing, routeRecordingDraft]);

  // ── Render ─────────────────────────────────────────────────────────────────

  if (isLoading) {
    return <div className={styles.loading}><Spinner size="lg" /></div>;
  }

  if (!config || !form) {
    return <div className={styles.page}>No configuration found for &quot;{appId}&quot;.</div>;
  }

  return (
    <div className={styles.page}>
      <div className={styles.toolbar}>
        <button className={styles.backBtn} onClick={() => navigate('/admin/appconfig')}><ArrowLeft size={14} /> Back</button>
        {editing ? (
          <div className={styles.toolbarActions}>
            <Button onClick={handleSave} disabled={updateConfig.isPending}>
              {updateConfig.isPending ? 'Saving\u2026' : 'Save'}
            </Button>
            <button className={styles.cancelBtn} onClick={cancelEditing}>Cancel</button>
          </div>
        ) : (
          <button className={styles.editBtn} onClick={startEditing}><Pencil size={14} /> Edit</button>
        )}
      </div>

      <div className={styles.header}>
        <h2 className={styles.title}><MonoText size="md">{appId}</MonoText></h2>
        <div className={styles.meta}>
          Version <MonoText size="xs">{config.version}</MonoText>
          {config.updatedAt && <> &middot; Updated {formatTimestamp(config.updatedAt)}</>}
        </div>
      </div>

      {/* ── Settings ──────────────────────────────────────────────────── */}
      <div className={styles.section}>
        <SectionHeader>Settings</SectionHeader>
        <div className={styles.settingsGrid}>
          <div className={styles.field}>
            <label className={styles.label}>App Log Level</label>
            {editing ? (
              <select className={styles.select} value={String(form.applicationLogLevel)}
                onChange={(e) => updateField('applicationLogLevel', e.target.value)}>
                <option value="ERROR">ERROR</option>
                <option value="WARN">WARN</option>
                <option value="INFO">INFO</option>
                <option value="DEBUG">DEBUG</option>
                <option value="TRACE">TRACE</option>
              </select>
            ) : (
              <Badge label={String(form.applicationLogLevel)} color={logLevelColor(form.applicationLogLevel as string)} variant="filled" />
            )}
          </div>
          <div className={styles.field}>
            <label className={styles.label}>Agent Log Level</label>
            {editing ? (
              <select className={styles.select} value={String(form.agentLogLevel ?? 'INFO')}
                onChange={(e) => updateField('agentLogLevel', e.target.value)}>
                <option value="ERROR">ERROR</option>
                <option value="WARN">WARN</option>
                <option value="INFO">INFO</option>
                <option value="DEBUG">DEBUG</option>
                <option value="TRACE">TRACE</option>
              </select>
            ) : (
              <Badge label={String(form.agentLogLevel ?? 'INFO')} color={logLevelColor(form.agentLogLevel as string)} variant="filled" />
            )}
          </div>
          <div className={styles.field}>
            <label className={styles.label}>Engine Level</label>
            {editing ? (
              <select className={styles.select} value={String(form.engineLevel)}
                onChange={(e) => updateField('engineLevel', e.target.value)}>
                <option value="NONE">None</option>
                <option value="MINIMAL">Minimal</option>
                <option value="REGULAR">Regular</option>
                <option value="COMPLETE">Complete</option>
              </select>
            ) : (
              <Badge label={String(form.engineLevel)} color={engineLevelColor(form.engineLevel as string)} variant="filled" />
            )}
          </div>
          <div className={styles.field}>
            <label className={styles.label}>Payload Capture</label>
            {editing ? (
              <select className={styles.select} value={String(form.payloadCaptureMode)}
                onChange={(e) => updateField('payloadCaptureMode', e.target.value)}>
                <option value="NONE">None</option>
                <option value="INPUT">Input</option>
                <option value="OUTPUT">Output</option>
                <option value="BOTH">Both</option>
              </select>
            ) : (
              <Badge label={String(form.payloadCaptureMode)} color={payloadColor(form.payloadCaptureMode as string)} variant="filled" />
            )}
          </div>
          <div className={styles.field}>
            <label className={styles.label}>Metrics</label>
            {editing ? (
              <label className={styles.toggleRow}>
                <input type="checkbox" checked={Boolean(form.metricsEnabled)}
                  onChange={(e) => updateField('metricsEnabled', e.target.checked)} />
                <span>{form.metricsEnabled ? 'Enabled' : 'Disabled'}</span>
              </label>
            ) : (
              <Badge label={form.metricsEnabled ? 'On' : 'Off'} color={form.metricsEnabled ? 'success' : 'error'} variant="filled" />
            )}
          </div>
          <div className={styles.field}>
            <label className={styles.label}>Sampling Rate</label>
            {editing ? (
              <input type="number" className={styles.select} min={0} max={1} step={0.01}
                value={form.samplingRate ?? 1.0}
                onChange={(e) => updateField('samplingRate', parseFloat(e.target.value) || 0)} />
            ) : (
              <MonoText size="xs">{form.samplingRate}</MonoText>
            )}
          </div>
          <div className={styles.field}>
            <label className={styles.label}>Compress Success</label>
            {editing ? (
              <label className={styles.toggleRow}>
                <input type="checkbox" checked={Boolean(form.compressSuccess)}
                  onChange={(e) => updateField('compressSuccess', e.target.checked)} />
                <span>{form.compressSuccess ? 'On' : 'Off'}</span>
              </label>
            ) : (
              <Badge label={form.compressSuccess ? 'On' : 'Off'} color={form.compressSuccess ? 'success' : 'error'} variant="filled" />
            )}
          </div>
        </div>
      </div>

      {/* ── Traces & Taps ─────────────────────────────────────────────── */}
      <div className={styles.section}>
        <SectionHeader>Traces &amp; Taps</SectionHeader>
        <span className={styles.sectionSummary}>
          {tracedCount} traced &middot; {tapCount} taps &middot; manage taps on route pages
        </span>
        {tracedTapRows.length > 0 ? (
          <DataTable<TracedTapRow> columns={tracedTapColumns} data={tracedTapRows} pageSize={20} />
        ) : (
          <span className={styles.hint}>
            No processors are individually traced and no taps are defined.
            {!editing && ' Enable tracing per-processor on the exchange detail page, or add taps on route pages.'}
          </span>
        )}
      </div>

      {/* ── Route Recording ───────────────────────────────────────────── */}
      <div className={styles.section}>
        <SectionHeader>Route Recording</SectionHeader>
        <span className={styles.sectionSummary}>
          {recordingCount} of {routeRecordingRows.length} routes recording
        </span>
        {routeRecordingRows.length > 0 ? (
          <DataTable<RouteRecordingRow> columns={routeRecordingColumns} data={routeRecordingRows} pageSize={20} />
        ) : (
          <span className={styles.hint}>
            No routes found for this application. Routes appear once agents report data.
          </span>
        )}
      </div>
    </div>
  );
}
