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, Select, Label, Tag, 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 { useEnvironmentStore } from '../../api/environment-store';
import { useCatalog } from '../../api/queries/catalog';
import type { CatalogApp, CatalogRoute } from '../../api/queries/catalog';
import { applyTracedProcessorUpdate, applyRouteRecordingUpdate } from '../../utils/config-draft-utils';
import styles from './AppConfigDetailPage.module.css';
import sectionStyles from '../../styles/section-card.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 selectedEnv = useEnvironmentStore((s) => s.environment);
  const { data: config, isLoading } = useApplicationConfig(appId, selectedEnv);
  const updateConfig = useUpdateApplicationConfig();
  const { data: catalog } = useCatalog();

  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>>({});
  const [sensitiveKeysDraft, setSensitiveKeysDraft] = useState<string[]>([]);
  const [sensitiveKeyInput, setSensitiveKeyInput] = useState('');

  // Find routes for this application from the catalog
  const appRoutes: CatalogRoute[] = useMemo(() => {
    if (!catalog || !appId) return [];
    const entry = (catalog as CatalogApp[]).find((e) => e.slug === 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 ?? 'BOTH',
        metricsEnabled: config.metricsEnabled,
        samplingRate: config.samplingRate,
        compressSuccess: config.compressSuccess,
      });
      setTracedDraft({ ...config.tracedProcessors });
      setRouteRecordingDraft({ ...config.routeRecording });
      setSensitiveKeysDraft([...(config.sensitiveKeys ?? [])]);
    }
  }, [config]);

  function startEditing() {
    if (!config) return;
    setForm({
      applicationLogLevel: config.applicationLogLevel ?? 'INFO',
      agentLogLevel: config.agentLogLevel ?? 'INFO',
      engineLevel: config.engineLevel ?? 'REGULAR',
      payloadCaptureMode: config.payloadCaptureMode ?? 'BOTH',
      metricsEnabled: config.metricsEnabled,
      samplingRate: config.samplingRate,
      compressSuccess: config.compressSuccess,
    });
    setTracedDraft({ ...config.tracedProcessors });
    setRouteRecordingDraft({ ...config.routeRecording });
    setSensitiveKeysDraft([...(config.sensitiveKeys ?? [])]);
    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) => applyTracedProcessorUpdate(prev, processorId, mode));
  }

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

  function handleSave() {
    if (!config || !form) return;
    const updated: ApplicationConfig = {
      ...config,
      ...form,
      tracedProcessors: tracedDraft,
      routeRecording: routeRecordingDraft,
      sensitiveKeys: sensitiveKeysDraft.length > 0 ? sensitiveKeysDraft : undefined,
    } as ApplicationConfig;
    updateConfig.mutate({ config: updated, environment: selectedEnv }, {
      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: 'Failed to save configuration', 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
              value={row.captureMode}
              onChange={(e) => updateTracedProcessor(row.processorId, e.target.value)}
              options={[
                { value: 'NONE', label: 'None' },
                { value: 'INPUT', label: 'Input' },
                { value: 'OUTPUT', label: 'Output' },
                { value: 'BOTH', label: 'Both' },
              ]}
            />
          );
        }
        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 variant="ghost" size="sm" 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 variant="ghost" size="sm" onClick={() => navigate('/admin/appconfig')}><ArrowLeft size={14} /> Back</Button>
        {editing ? (
          <div className={styles.toolbarActions}>
            <Button variant="ghost" size="sm" onClick={cancelEditing}>Cancel</Button>
            <Button variant="primary" size="sm" onClick={handleSave} loading={updateConfig.isPending}>Save</Button>
          </div>
        ) : (
          <Button variant="secondary" size="sm" onClick={startEditing}><Pencil size={14} /> Edit</Button>
        )}
      </div>

      {editing && (
        <div className={styles.editBanner}>
          Editing configuration. Changes are not saved until you click Save.
        </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={sectionStyles.section}>
        <SectionHeader>Settings</SectionHeader>
        <div className={styles.settingsGrid}>
          <div className={styles.field}>
            <Label>App Log Level</Label>
            {editing ? (
              <Select value={String(form.applicationLogLevel)}
                onChange={(e) => updateField('applicationLogLevel', e.target.value)}
                options={[
                  { value: 'ERROR', label: 'ERROR' },
                  { value: 'WARN', label: 'WARN' },
                  { value: 'INFO', label: 'INFO' },
                  { value: 'DEBUG', label: 'DEBUG' },
                  { value: 'TRACE', label: 'TRACE' },
                ]}
              />
            ) : (
              <Badge label={String(form.applicationLogLevel)} color={logLevelColor(form.applicationLogLevel as string)} variant="filled" />
            )}
          </div>
          <div className={styles.field}>
            <Label>Agent Log Level</Label>
            {editing ? (
              <Select value={String(form.agentLogLevel ?? 'INFO')}
                onChange={(e) => updateField('agentLogLevel', e.target.value)}
                options={[
                  { value: 'ERROR', label: 'ERROR' },
                  { value: 'WARN', label: 'WARN' },
                  { value: 'INFO', label: 'INFO' },
                  { value: 'DEBUG', label: 'DEBUG' },
                  { value: 'TRACE', label: 'TRACE' },
                ]}
              />
            ) : (
              <Badge label={String(form.agentLogLevel ?? 'INFO')} color={logLevelColor(form.agentLogLevel as string)} variant="filled" />
            )}
          </div>
          <div className={styles.field}>
            <Label>Engine Level</Label>
            {editing ? (
              <Select value={String(form.engineLevel)}
                onChange={(e) => updateField('engineLevel', e.target.value)}
                options={[
                  { value: 'NONE', label: 'None' },
                  { value: 'MINIMAL', label: 'Minimal' },
                  { value: 'REGULAR', label: 'Regular' },
                  { value: 'COMPLETE', label: 'Complete' },
                ]}
              />
            ) : (
              <Badge label={String(form.engineLevel)} color={engineLevelColor(form.engineLevel as string)} variant="filled" />
            )}
          </div>
          <div className={styles.field}>
            <Label>Payload Capture</Label>
            {editing ? (
              <Select value={String(form.payloadCaptureMode)}
                onChange={(e) => updateField('payloadCaptureMode', e.target.value)}
                options={[
                  { value: 'NONE', label: 'None' },
                  { value: 'INPUT', label: 'Input' },
                  { value: 'OUTPUT', label: 'Output' },
                  { value: 'BOTH', label: 'Both' },
                ]}
              />
            ) : (
              <Badge label={String(form.payloadCaptureMode)} color={payloadColor(form.payloadCaptureMode as string)} variant="filled" />
            )}
          </div>
          <div className={styles.field}>
            <Label>Metrics</Label>
            {editing ? (
              <Toggle
                checked={Boolean(form.metricsEnabled)}
                onChange={(e) => updateField('metricsEnabled', (e.target as HTMLInputElement).checked)}
                label={form.metricsEnabled ? 'Enabled' : 'Disabled'}
              />
            ) : (
              <Badge label={form.metricsEnabled ? 'On' : 'Off'} color={form.metricsEnabled ? 'success' : 'error'} variant="filled" />
            )}
          </div>
          <div className={styles.field}>
            <Label>Sampling Rate</Label>
            {editing ? (
              <input type="number" className={styles.numberInput} 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>Compress Success</Label>
            {editing ? (
              <Toggle
                checked={Boolean(form.compressSuccess)}
                onChange={(e) => updateField('compressSuccess', (e.target as HTMLInputElement).checked)}
                label={form.compressSuccess ? 'On' : 'Off'}
              />
            ) : (
              <Badge label={form.compressSuccess ? 'On' : 'Off'} color={form.compressSuccess ? 'success' : 'error'} variant="filled" />
            )}
          </div>
        </div>
      </div>

      {/* ── Traces & Taps ─────────────────────────────────────────────── */}
      <div className={sectionStyles.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={sectionStyles.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>

      {/* ── Sensitive Keys ──────────────────────────────────────────────────── */}
      <div className={sectionStyles.section}>
        <SectionHeader>Sensitive Keys</SectionHeader>
        {config.globalSensitiveKeys && config.globalSensitiveKeys.length > 0 ? (
          <span className={styles.sectionSummary}>
            {config.globalSensitiveKeys.length} global (enforced) &middot; {(editing ? sensitiveKeysDraft : config.sensitiveKeys ?? []).length} app-specific
          </span>
        ) : (
          <span className={styles.sectionSummary}>
            No global sensitive keys configured. Agents use their built-in defaults.
          </span>
        )}

        {/* Global keys — read-only pills */}
        {config.globalSensitiveKeys && config.globalSensitiveKeys.length > 0 && (
          <div className={styles.sensitiveKeysRow}>
            <Label>Global (enforced)</Label>
            <div className={styles.pillList}>
              {config.globalSensitiveKeys.map((key) => (
                <Badge key={key} label={key} color="auto" variant="filled" />
              ))}
            </div>
          </div>
        )}

        {/* Per-app keys — editable */}
        <div className={styles.sensitiveKeysRow}>
          <Label>Application-specific</Label>
          <div className={styles.pillList}>
            {(editing ? sensitiveKeysDraft : config.sensitiveKeys ?? []).map((key, i) => (
              editing ? (
                <Tag key={`${key}-${i}`} label={key} onRemove={() => setSensitiveKeysDraft((prev) => prev.filter((_, idx) => idx !== i))} />
              ) : (
                <Badge key={key} label={key} color="primary" variant="filled" />
              )
            ))}
            {(editing ? sensitiveKeysDraft : config.sensitiveKeys ?? []).length === 0 && (
              <span className={styles.hint}>None</span>
            )}
          </div>
          {editing && (
            <div className={styles.sensitiveKeyInput}>
              <input
                className={styles.numberInput}
                style={{ flex: 1 }}
                value={sensitiveKeyInput}
                onChange={(e) => setSensitiveKeyInput(e.target.value)}
                onKeyDown={(e) => {
                  if (e.key === 'Enter') {
                    e.preventDefault();
                    const trimmed = sensitiveKeyInput.trim();
                    if (trimmed && !sensitiveKeysDraft.some((k) => k.toLowerCase() === trimmed.toLowerCase())) {
                      setSensitiveKeysDraft((prev) => [...prev, trimmed]);
                      setSensitiveKeyInput('');
                    }
                  }
                }}
                placeholder="Add key or glob pattern"
              />
            </div>
          )}
        </div>

        {config.globalSensitiveKeys && config.globalSensitiveKeys.length > 0 && (
          <span className={styles.hint}>
            Global keys are enforced by your administrator and cannot be removed per-app.
          </span>
        )}
      </div>
    </div>
  );
}
