'use client';

import { useMemo, useState } from 'react';
import { useQuery } from '@tanstack/react-query';
import type { LogRecord } from '@zyphon/shared-types';
import { adminClient } from '@/lib/api';
import { formatCount } from '@/lib/format';
import { Card } from '@/components/ui/Card';
import { Tabs } from '@/components/ui/Tabs';
import { EmptyState } from '@/components/ui/EmptyState';
import { Input } from '@/components/ui/Input';
import { Select } from '@/components/ui/Select';
import { Button } from '@/components/ui/Button';
import { Badge } from '@/components/ui/Badge';
import { SkeletonBlock } from '@/components/ui/Skeleton';
import { Table, Th, Td } from '@/components/ui/Table';
import { useToast } from '@/components/Toast';

type LogFormat = 'structured' | 'json' | 'ndjson' | 'txt';
type ExportExt = 'log' | 'json' | 'txt';

function recordStatus(r: LogRecord): string {
  if ('status' in r && r.status) return String(r.status);
  if ('event' in r && r.event) return String(r.event);
  if ('ev' in r && r.ev) return String(r.ev);
  if ('type' in r && r.type) return String(r.type);
  return 'event';
}

function recordApp(r: LogRecord): string {
  if ('application' in r && r.application) return String(r.application);
  return 'System';
}

function formatTxtLine(r: LogRecord): string {
  const ts = r.ts ? new Date(Number(r.ts)).toISOString() : new Date().toISOString();
  const st = recordStatus(r).toUpperCase().padEnd(9, ' ');
  const app = recordApp(r);
  if ('channel' in r) {
    const ch = String(r.channel).toUpperCase();
    const via = r.provider || 'none';
    const to = r.recipient_masked || '—';
    const ms = r.latency_ms ?? 0;
    const err = r.error?.message ? ` | ERROR="${r.error.message.trim()}"` : '';
    return `[${ts}] [${st}] [app=${app}] [channel=${ch}] [via=${via}] [to=${to}] [${ms}ms] id=${r.message_id}${err}`;
  }
  return `[${ts}] [${st}] ${JSON.stringify(r)}`;
}

function downloadBlob(filename: string, content: string, mime = 'text/plain;charset=utf-8') {
  const blob = new Blob([content], { type: mime });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = filename;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  URL.revokeObjectURL(url);
}

export default function LogsPage() {
  const { toast } = useToast();
  const [tab, setTab] = useState<'live' | 'files'>('live');
  const [selectedApp, setSelectedApp] = useState<string>('');
  const [format, setFormat] = useState<LogFormat>('structured');
  const [exportExt, setExportExt] = useState<ExportExt>('json');
  const [q, setQ] = useState('');
  const [level, setLevel] = useState('');
  const [viewingFile, setViewingFile] = useState<string | null>(null);
  const [fileFormat, setFileFormat] = useState<'json' | 'ndjson' | 'txt'>('json');

  const { data: apps = [] } = useQuery({
    queryKey: ['applications'],
    queryFn: () => adminClient.getApplications(),
  });

  const liveQuery = useQuery({
    queryKey: ['logs', 'live', q],
    queryFn: () =>
      adminClient.searchLogs({
        q: q || undefined,
        limit: 150,
      }),
    refetchInterval: tab === 'live' ? 3500 : false,
  });

  const filesQuery = useQuery({
    queryKey: ['logs', 'files'],
    queryFn: () => adminClient.getLogFiles(),
  });

  const fileContentQuery = useQuery({
    queryKey: ['logs', 'file-content', viewingFile, selectedApp],
    queryFn: () => adminClient.getLogFileContent(viewingFile!, selectedApp || undefined),
    enabled: Boolean(viewingFile),
  });

  const allRecords = useMemo(() => liveQuery.data?.records || [], [liveQuery.data]);

  const appOptions = useMemo(() => {
    const counts = new Map<string, number>();
    for (const a of apps) {
      counts.set(a.name, 0);
    }
    for (const r of allRecords) {
      const appName = recordApp(r);
      counts.set(appName, (counts.get(appName) || 0) + 1);
    }
    return Array.from(counts.entries()).map(([name, count]) => ({ name, count }));
  }, [apps, allRecords]);

  const filtered = useMemo(() => {
    return allRecords.filter((r) => {
      if (selectedApp && recordApp(r) !== selectedApp) return false;
      if (level && !recordStatus(r).toLowerCase().includes(level.toLowerCase())) return false;
      return true;
    });
  }, [allRecords, selectedApp, level]);

  const formattedText = useMemo(() => {
    if (format === 'json') return JSON.stringify(filtered, null, 2);
    if (format === 'ndjson') return filtered.map((r) => JSON.stringify(r)).join('\n');
    if (format === 'txt') return filtered.map((r) => formatTxtLine(r)).join('\n');
    return '';
  }, [filtered, format]);

  const handleCopyLogs = () => {
    const text =
      format === 'structured'
        ? JSON.stringify(filtered, null, 2)
        : formattedText;
    void navigator.clipboard?.writeText(text);
    toast(`Copied ${filtered.length} log entries`);
  };

  const handleDownloadLive = async (ext: ExportExt) => {
    const appSlug = selectedApp ? selectedApp.toLowerCase().replace(/\s+/g, '-') : 'all-apps';
    try {
      const res = await adminClient.searchLogs({
        app: selectedApp || undefined,
        q: q || undefined,
        limit: 500,
      });
      const records = (res.records || []).filter((r) => {
        if (selectedApp && recordApp(r) !== selectedApp) return false;
        if (level && !recordStatus(r).toLowerCase().includes(level.toLowerCase())) return false;
        return true;
      });
      if (!records.length) {
        toast(`No log entries found for ${selectedApp || 'All Applications'}`);
        return;
      }
      const content =
        ext === 'json'
          ? JSON.stringify(records, null, 2)
          : ext === 'log'
          ? records.map((r) => JSON.stringify(r)).join('\n')
          : records.map((r) => formatTxtLine(r)).join('\n');
      downloadBlob(`zyphon-${appSlug}-logs.${ext}`, content);
      toast(`Downloaded ${records.length} logs for ${selectedApp || 'All Applications'} (.${ext})`);
    } catch (err: any) {
      toast(err.message || 'Could not export logs');
    }
  };

  const handleDownloadFile = async (filename: string, ext: ExportExt) => {
    try {
      const data = await adminClient.getLogFileContent(filename, selectedApp || undefined);
      if (!data.records.length) {
        toast(`No records for ${selectedApp || 'this file'} in ${filename}`);
        return;
      }
      const base = filename.replace(/\.log$/, '');
      const suffix = selectedApp ? `-${selectedApp.toLowerCase().replace(/\s+/g, '-')}` : '';
      const outBase = `${base}${suffix}`;
      if (ext === 'log') {
        downloadBlob(`${outBase}.log`, data.raw);
      } else if (ext === 'json') {
        downloadBlob(`${outBase}.json`, JSON.stringify(data.records, null, 2), 'application/json');
      } else {
        downloadBlob(`${outBase}.txt`, data.records.map((r) => formatTxtLine(r)).join('\n'));
      }
      toast(`Downloaded ${outBase}.${ext} (${data.records.length} records)`);
    } catch (err: any) {
      toast(err.message || 'Could not download log file');
    }
  };

  return (
    <div>
      {/* Header with ONE unified Download control */}
      <div className="flex items-end justify-between gap-3 mb-4 flex-wrap">
        <div>
          <h1 className="text-2xl font-bold tracking-tight m-0">Application & Gateway Logs</h1>
          <div className="text-muted text-xs mt-0.5">
            Select an application below to filter the live stream and downloads.
          </div>
        </div>
        <div className="flex items-center gap-2 flex-wrap">
          <Button size="sm" onClick={handleCopyLogs}>
            Copy {format.toUpperCase()}
          </Button>
          <div className="inline-flex items-center rounded-[var(--r)] border border-line bg-surface overflow-hidden">
            <Select
              className="w-auto border-0 rounded-none text-xs py-1.5 pl-2.5 pr-6 bg-transparent"
              value={exportExt}
              onChange={(e) => setExportExt(e.target.value as ExportExt)}
              aria-label="Export format"
            >
              <option value="json">Format: .JSON</option>
              <option value="txt">Format: .TXT</option>
              <option value="log">Format: .LOG (NDJSON)</option>
            </Select>
            <Button
              size="sm"
              variant="primary"
              className="rounded-none border-0 border-l border-line"
              onClick={() => void handleDownloadLive(exportExt)}
            >
              Download {selectedApp ? `${selectedApp}` : 'All'}
            </Button>
          </div>
        </div>
      </div>

      {/* Scalable Application Filter Bar (no duplicate download buttons) */}
      <div className="zy-card-3d p-3.5 mb-4">
        <div className="flex items-center justify-between gap-2 flex-wrap mb-2">
          <div className="text-xs font-semibold uppercase tracking-wider text-muted">
            Application Filter
          </div>
          {appOptions.length > 6 && (
            <Select
              className="w-auto text-xs py-1"
              value={selectedApp}
              onChange={(e) => setSelectedApp(e.target.value)}
              aria-label="Quick select application"
            >
              <option value="">All Applications ({formatCount(allRecords.length)})</option>
              {appOptions.map((a) => (
                <option key={a.name} value={a.name}>
                  {a.name} ({formatCount(a.count)})
                </option>
              ))}
            </Select>
          )}
        </div>
        <div className="flex items-center gap-2 overflow-x-auto pb-1">
          <button
            type="button"
            onClick={() => setSelectedApp('')}
            title={`${allRecords.length} total log records`}
            className={`px-3 py-1.5 rounded-[var(--r)] text-xs font-semibold border transition-all cursor-pointer whitespace-nowrap ${
              selectedApp === ''
                ? 'bg-accent text-on-accent border-accent'
                : 'bg-bg text-text border-line hover:bg-surface-2'
            }`}
          >
            All Applications ({formatCount(allRecords.length)})
          </button>
          {appOptions.map((a) => (
            <button
              key={a.name}
              type="button"
              onClick={() => setSelectedApp(a.name)}
              title={`${a.count} log records for ${a.name}`}
              className={`px-3 py-1.5 rounded-[var(--r)] text-xs font-semibold border transition-all cursor-pointer flex items-center gap-1.5 whitespace-nowrap ${
                selectedApp === a.name
                  ? 'bg-accent text-on-accent border-accent'
                  : 'bg-bg text-text border-line hover:bg-surface-2'
              }`}
            >
              <span>{a.name}</span>
              <span className="opacity-80 font-mono">({formatCount(a.count)})</span>
            </button>
          ))}
        </div>
      </div>

      <Tabs
        tabs={[
          { id: 'live', label: 'Live Stream' },
          { id: 'files', label: 'Log Archives & Download' },
        ]}
        value={tab}
        onChange={setTab}
      />

      {tab === 'live' ? (
        <>
          <div className="flex gap-2.5 items-center justify-between flex-wrap mb-3">
            <div className="flex gap-2 flex-wrap flex-1">
              <Input
                className="max-w-sm"
                placeholder="Search recipient, provider, message ID, error…"
                value={q}
                onChange={(e) => setQ(e.target.value)}
              />
              <Select className="w-auto" value={level} onChange={(e) => setLevel(e.target.value)}>
                <option value="">All statuses</option>
                <option value="delivered">Delivered</option>
                <option value="sent">Sent</option>
                <option value="failed">Failed</option>
                <option value="blocked">Blocked</option>
              </Select>
            </div>

            {/* Format Switcher: Structured | JSON | NDJSON | TXT */}
            <div className="inline-flex rounded-[var(--r)] border border-line bg-surface p-1 gap-1">
              {(['structured', 'json', 'ndjson', 'txt'] as LogFormat[]).map((fmt) => (
                <button
                  key={fmt}
                  type="button"
                  onClick={() => setFormat(fmt)}
                  className={`px-2.5 py-1 rounded-[calc(var(--r)-2px)] text-xs font-mono font-semibold uppercase cursor-pointer border-0 ${
                    format === fmt
                      ? 'bg-accent text-on-accent'
                      : 'bg-transparent text-muted hover:text-text'
                  }`}
                >
                  {fmt}
                </button>
              ))}
            </div>
          </div>

          <Card className="p-3">
            {liveQuery.isLoading ? (
              <SkeletonBlock rows={5} />
            ) : liveQuery.isError ? (
              <EmptyState
                title="Could not load live logs"
                description="Check that the gateway is running and click retry."
                action={<Button onClick={() => liveQuery.refetch()}>Retry</Button>}
              />
            ) : !filtered.length ? (
              <EmptyState
                title="No logs for this filter"
                description="Send a message or switch application filters to view activity."
              />
            ) : format === 'structured' ? (
              <div className="font-mono text-[12.5px] divide-y divide-line">
                {filtered.map((r: any, i) => {
                  const status = recordStatus(r);
                  const isOk = status === 'delivered' || status === 'sent';
                  const isErr = status === 'failed' || status === 'blocked';
                  return (
                    <div
                      key={r.message_id || r.msgId || i}
                      className="py-2.5 px-2 hover:bg-surface-2 rounded transition-colors flex flex-col gap-1"
                    >
                      <div className="flex items-center justify-between gap-2 flex-wrap">
                        <div className="flex items-center gap-2 flex-wrap">
                          <span className="text-muted text-xs">
                            {r.ts ? new Date(Number(r.ts)).toLocaleTimeString() : '—'}
                          </span>
                          <Badge tone={isOk ? 'ok' : isErr ? 'danger' : 'accent'}>
                            {status.toUpperCase()}
                          </Badge>
                          <Badge tone="muted">{recordApp(r)}</Badge>
                          {r.channel && (
                            <span className="uppercase text-xs font-bold text-accent">
                              {r.channel}
                            </span>
                          )}
                          {r.recipient_masked && (
                            <span className="text-text font-semibold">→ {r.recipient_masked}</span>
                          )}
                        </div>
                        <div className="flex items-center gap-2 text-xs text-muted">
                          {r.provider && <span>via {r.provider}</span>}
                          {r.latency_ms != null && <span>· {r.latency_ms}ms</span>}
                          {r.message_id && <span>· {r.message_id}</span>}
                        </div>
                      </div>
                      {(r.subject || r.body_preview || r.error?.message) && (
                        <div className="text-xs text-muted pl-2 border-l-2 border-line mt-0.5 truncate">
                          {r.subject ? <b className="text-text">{r.subject}: </b> : null}
                          {r.error?.message ? (
                            <span className="text-danger">{r.error.message}</span>
                          ) : (
                            r.body_preview
                          )}
                        </div>
                      )}
                    </div>
                  );
                })}
              </div>
            ) : (
              <pre className="m-0 p-4 rounded-[var(--r)] bg-bg border border-line font-mono text-xs text-text overflow-x-auto max-h-[540px] leading-relaxed">
                {formattedText}
              </pre>
            )}
          </Card>
        </>
      ) : (
        <>
          <Card className="p-3">
            {selectedApp && (
              <div className="flex items-center justify-between gap-2 px-3 py-2 mb-3 rounded-[var(--r)] bg-surface-2 border border-line text-xs">
                <span>
                  Showing archives filtered for application: <b className="text-text">{selectedApp}</b>
                </span>
                <button
                  type="button"
                  onClick={() => setSelectedApp('')}
                  className="text-accent hover:underline bg-transparent border-0 cursor-pointer font-semibold"
                >
                  Show all applications
                </button>
              </div>
            )}

            {filesQuery.isLoading ? (
              <SkeletonBlock rows={3} />
            ) : !(filesQuery.data?.files || []).length ? (
              <EmptyState
                title="No log files yet"
                description="Logs are written automatically as messages are dispatched."
              />
            ) : (
              <Table>
                <thead>
                  <tr>
                    <Th>File</Th>
                    <Th>Date (UTC)</Th>
                    <Th>Application Breakdown</Th>
                    <Th>Size</Th>
                    <Th>Actions</Th>
                  </tr>
                </thead>
                <tbody>
                  {(filesQuery.data?.files || []).map((f: Record<string, unknown>) => {
                    const filename = String(f.filename);
                    const byApp = (f.by_app || {}) as Record<string, number>;
                    const appEntries = Object.entries(byApp);
                    const hasRecordsForSelectedApp =
                      !selectedApp || (byApp[selectedApp] ?? 0) > 0;
                    return (
                      <tr key={filename}>
                        <Td className="font-mono font-semibold text-text">{filename}</Td>
                        <Td className="font-mono text-muted">{String(f.day)}</Td>
                        <Td>
                          <div className="flex items-center gap-1.5 flex-wrap">
                            {appEntries.length > 0 ? (
                              appEntries.map(([appName, count]) => (
                                <span key={appName} title={`${count} records`}>
                                  <Badge
                                    tone={selectedApp === appName ? 'accent' : 'muted'}
                                  >
                                    {appName}: {formatCount(count)}
                                  </Badge>
                                </span>
                              ))
                            ) : (
                              <span className="text-xs text-muted">0 records</span>
                            )}
                          </div>
                        </Td>
                        <Td className="text-muted font-mono">
                          {typeof f.sizeBytes === 'number'
                            ? `${Math.max(1, Math.round(f.sizeBytes / 1024))} KB`
                            : '—'}
                        </Td>
                        <Td>
                          <div className="flex items-center gap-1.5 flex-wrap">
                            <Button
                              size="sm"
                              variant="primary"
                              disabled={!hasRecordsForSelectedApp}
                              onClick={() => setViewingFile(filename)}
                            >
                              View
                            </Button>
                            <Button
                              size="sm"
                              disabled={!hasRecordsForSelectedApp}
                              onClick={() => void handleDownloadFile(filename, exportExt)}
                            >
                              Download .{exportExt}
                            </Button>
                          </div>
                        </Td>
                      </tr>
                    );
                  })}
                </tbody>
              </Table>
            )}
          </Card>

          {viewingFile && (
            <Card>
              <div className="flex items-center justify-between gap-3 mb-3 flex-wrap">
                <div className="flex items-center gap-2">
                  <b className="font-mono text-sm text-text">{viewingFile}</b>
                  {selectedApp && <Badge tone="accent">{selectedApp}</Badge>}
                  <Badge tone="muted">
                    {formatCount((fileContentQuery.data?.records || []).length)} records
                  </Badge>
                </div>
                <div className="flex items-center gap-2">
                  <div className="inline-flex rounded-[var(--r)] border border-line bg-bg p-0.5 gap-1">
                    {(['json', 'ndjson', 'txt'] as const).map((fmt) => (
                      <button
                        key={fmt}
                        type="button"
                        onClick={() => setFileFormat(fmt)}
                        className={`px-2.5 py-1 rounded text-xs font-mono uppercase cursor-pointer border-0 ${
                          fileFormat === fmt
                            ? 'bg-accent text-on-accent'
                            : 'bg-transparent text-muted'
                        }`}
                      >
                        {fmt}
                      </button>
                    ))}
                  </div>
                  <Button
                    size="sm"
                    onClick={() =>
                      void handleDownloadFile(
                        viewingFile,
                        fileFormat === 'ndjson' ? 'log' : fileFormat,
                      )
                    }
                  >
                    Download
                  </Button>
                  <Button size="sm" variant="ghost" onClick={() => setViewingFile(null)}>
                    Close ✕
                  </Button>
                </div>
              </div>
              {fileContentQuery.isLoading ? (
                <SkeletonBlock rows={5} />
              ) : (
                <pre className="m-0 p-4 rounded-[var(--r)] bg-bg border border-line font-mono text-xs text-text overflow-x-auto max-h-[480px] leading-relaxed">
                  {fileFormat === 'json'
                    ? JSON.stringify(fileContentQuery.data?.records || [], null, 2)
                    : fileFormat === 'ndjson'
                    ? fileContentQuery.data?.raw || ''
                    : (fileContentQuery.data?.records || [])
                        .map((r) => formatTxtLine(r))
                        .join('\n')}
                </pre>
              )}
            </Card>
          )}
        </>
      )}
    </div>
  );
}
