import {Download, Eye, RefreshCcw, Search, X} from 'lucide-react';
import {FormEvent, useEffect, useMemo, useState} from 'react';

import {listAdminTrackingEvents} from '../api/adminApiClient';
import type {
  AdminTrackingEventEntryPayload,
  TrackingScopeKind,
} from '../api/adminApiTypes';
import {
  filterAdminTrackingEventDefinitions,
  findAdminTrackingEventDefinition,
} from '../config/trackingEventDefinitions';
import {handlePageError} from './pageUtils';

interface AdminTrackingEventsPageProps {
  token: string;
  onUnauthorized: (message?: string) => void;
}

const scopeKindOptions: Array<{value: TrackingScopeKind | ''; label: string}> = [
  {value: '', label: '全部'},
  {value: 'site', label: 'site'},
  {value: 'work', label: 'work'},
  {value: 'module', label: 'module'},
  {value: 'user', label: 'user'},
];

const exportColumns: Array<{
  key: keyof AdminTrackingEventEntryPayload;
  label: string;
}> = [
  {key: 'eventId', label: '事件 ID'},
  {key: 'eventKey', label: 'Event Key'},
  {key: 'eventTitle', label: '事件名称'},
  {key: 'scopeKind', label: 'Scope Kind'},
  {key: 'scopeId', label: 'Scope ID'},
  {key: 'dayKey', label: 'Day Key'},
  {key: 'userId', label: 'User ID'},
  {key: 'ownerUserId', label: 'Owner User ID'},
  {key: 'profileId', label: 'Profile ID'},
  {key: 'moduleKey', label: 'Module Key'},
  {key: 'metadataJson', label: 'Metadata JSON'},
  {key: 'occurredAt', label: '发生时间'},
];

export function AdminTrackingEventsPage({
  token,
  onUnauthorized,
}: AdminTrackingEventsPageProps) {
  const [entries, setEntries] = useState<AdminTrackingEventEntryPayload[]>([]);
  const [eventKey, setEventKey] = useState('');
  const [userId, setUserId] = useState('');
  const [scopeKind, setScopeKind] = useState<TrackingScopeKind | ''>('');
  const [scopeId, setScopeId] = useState('');
  const [limit, setLimit] = useState('200');
  const [errorMessage, setErrorMessage] = useState('');
  const [isLoading, setIsLoading] = useState(false);
  const [detailEntry, setDetailEntry] =
    useState<AdminTrackingEventEntryPayload | null>(null);

  useEffect(() => {
    void refreshTrackingEvents();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [token]);

  const filteredEventDefinitions = useMemo(
    () => filterAdminTrackingEventDefinitions(eventKey),
    [eventKey],
  );

  async function refreshTrackingEvents() {
    setIsLoading(true);
    setErrorMessage('');
    try {
      const response = await listAdminTrackingEvents(token, {
        eventKey,
        userId,
        scopeKind,
        scopeId,
        limit: parseLimit(limit),
      });
      setEntries(response.entries);
    } catch (error: unknown) {
      handlePageError(error, onUnauthorized, setErrorMessage);
    } finally {
      setIsLoading(false);
    }
  }

  function handleSearch(event: FormEvent<HTMLFormElement>) {
    event.preventDefault();
    void refreshTrackingEvents();
  }

  function handleExport() {
    if (!entries.length) {
      setErrorMessage('当前没有可导出的埋点数据');
      return;
    }
    exportTrackingEventsAsExcel(entries);
  }

  return (
    <section className="admin-page admin-page-wide">
      <div className="admin-page-heading">
        <div>
          <h2>埋点数据</h2>
          <p>原始事件明细</p>
        </div>
        <div className="admin-action-row">
          <button
            className="admin-secondary-button"
            disabled={isLoading}
            type="button"
            onClick={refreshTrackingEvents}
          >
            <RefreshCcw size={17} aria-hidden="true" />
            <span>{isLoading ? '刷新中' : '刷新'}</span>
          </button>
          <button
            className="admin-primary-button"
            disabled={!entries.length}
            type="button"
            onClick={handleExport}
          >
            <Download size={17} aria-hidden="true" />
            <span>导出 Excel</span>
          </button>
        </div>
      </div>

      <form className="admin-panel admin-form" onSubmit={handleSearch}>
        <div className="admin-filter-grid">
          <label className="admin-field">
            <span>Event Key</span>
            <input
              list="admin-tracking-event-keys"
              placeholder="全部"
              value={eventKey}
              onChange={(event) => setEventKey(event.target.value)}
            />
            <datalist id="admin-tracking-event-keys">
              {filteredEventDefinitions.map((definition) => (
                <option key={definition.key} value={definition.key}>
                  {definition.title}
                </option>
              ))}
            </datalist>
          </label>
          <label className="admin-field">
            <span>User ID</span>
            <input
              placeholder="全部"
              value={userId}
              onChange={(event) => setUserId(event.target.value)}
            />
          </label>
          <label className="admin-field">
            <span>Scope Kind</span>
            <select
              value={scopeKind}
              onChange={(event) =>
                setScopeKind(event.target.value as TrackingScopeKind | '')
              }
            >
              {scopeKindOptions.map((option) => (
                <option key={option.value || 'all'} value={option.value}>
                  {option.label}
                </option>
              ))}
            </select>
          </label>
          <label className="admin-field">
            <span>Scope ID</span>
            <input
              placeholder="全部"
              value={scopeId}
              onChange={(event) => setScopeId(event.target.value)}
            />
          </label>
          <label className="admin-field">
            <span>条数</span>
            <input
              inputMode="numeric"
              value={limit}
              onChange={(event) => setLimit(event.target.value)}
            />
          </label>
          <button className="admin-secondary-button" disabled={isLoading} type="submit">
            <Search size={17} aria-hidden="true" />
            <span>{isLoading ? '查询中' : '查询'}</span>
          </button>
        </div>
      </form>

      {errorMessage ? (
        <div className="admin-alert" role="status">
          {errorMessage}
        </div>
      ) : null}

      <div className="admin-panel">
        <div className="admin-panel-heading">
          <h3>事件明细</h3>
          <span>{entries.length} 条</span>
        </div>
        <div className="admin-table-wrap">
          <table className="admin-table admin-table-wide">
            <thead>
              <tr>
                <th>事件</th>
                <th>Scope</th>
                <th>用户</th>
                <th>归属</th>
                <th>Metadata</th>
                <th>时间</th>
                <th>详情</th>
              </tr>
            </thead>
            <tbody>
              {entries.length ? (
                entries.map((entry) => (
                  <tr key={entry.eventId}>
                    <td>
                      <strong>{resolveEventTitle(entry)}</strong>
                      <small>{entry.eventKey}</small>
                      <small>{entry.eventId}</small>
                    </td>
                    <td>
                      <span className="admin-status admin-status-pending">
                        {entry.scopeKind}
                      </span>
                      <small>{entry.scopeId || '-'}</small>
                      <small>dayKey: {entry.dayKey}</small>
                    </td>
                    <td>
                      {entry.userId || '-'}
                      <small>owner: {entry.ownerUserId || '-'}</small>
                    </td>
                    <td>
                      {entry.profileId || '-'}
                      <small>module: {entry.moduleKey || '-'}</small>
                    </td>
                    <td>
                      <pre className="admin-json-preview">
                        {formatMetadataJson(entry.metadataJson)}
                      </pre>
                    </td>
                    <td>{formatOccurredAt(entry.occurredAt)}</td>
                    <td>
                      <button
                        className="admin-secondary-button"
                        type="button"
                        onClick={() => setDetailEntry(entry)}
                      >
                        <Eye size={16} aria-hidden="true" />
                        <span>详情</span>
                      </button>
                    </td>
                  </tr>
                ))
              ) : (
                <tr>
                  <td colSpan={7}>{isLoading ? '正在加载' : '暂无数据'}</td>
                </tr>
              )}
            </tbody>
          </table>
        </div>
      </div>

      {detailEntry ? (
        <TrackingEventDetailPanel
          entry={detailEntry}
          onClose={() => setDetailEntry(null)}
        />
      ) : null}
    </section>
  );
}

function TrackingEventDetailPanel({
  entry,
  onClose,
}: {
  entry: AdminTrackingEventEntryPayload;
  onClose: () => void;
}) {
  return (
    <div className="admin-confirm-backdrop" role="presentation">
      <section
        aria-label="埋点详情"
        className="admin-detail-panel"
        role="dialog"
      >
        <div className="admin-panel-heading">
          <h3>{resolveEventTitle(entry)}</h3>
          <button
            aria-label="关闭详情"
            className="admin-ghost-button"
            type="button"
            onClick={onClose}
          >
            <X size={17} aria-hidden="true" />
          </button>
        </div>
        <dl className="admin-info-list admin-detail-list">
          {exportColumns.map((column) => (
            <div key={column.key}>
              <dt>{column.label}</dt>
              <dd>
                {column.key === 'metadataJson' ? (
                  <pre className="admin-code-block">
                    {formatMetadataJson(entry.metadataJson)}
                  </pre>
                ) : (
                  formatExportCell(entry[column.key], column.key) || '-'
                )}
              </dd>
            </div>
          ))}
        </dl>
      </section>
    </div>
  );
}

function parseLimit(value: string) {
  const parsed = Number.parseInt(value.trim(), 10);
  if (!Number.isFinite(parsed)) {
    return 200;
  }
  return Math.min(Math.max(parsed, 1), 1000);
}

function resolveEventTitle(entry: AdminTrackingEventEntryPayload) {
  return (
    findAdminTrackingEventDefinition(entry.eventKey)?.title ||
    entry.eventTitle ||
    entry.eventKey
  );
}

function formatMetadataJson(value: string) {
  const trimmed = value.trim();
  if (!trimmed) {
    return '-';
  }
  try {
    return JSON.stringify(JSON.parse(trimmed), null, 2);
  } catch {
    return trimmed;
  }
}

function exportTrackingEventsAsExcel(entries: AdminTrackingEventEntryPayload[]) {
  const tableRows = [
    exportColumns.map((column) => `<th>${escapeHtml(column.label)}</th>`).join(''),
    ...entries.map((entry) =>
      exportColumns
        .map(
          (column) =>
            `<td style="mso-number-format:'\\@';">${escapeHtml(formatExportCell(entry[column.key], column.key))}</td>`,
        )
        .join(''),
    ),
  ];
  const html = `\uFEFF<html><head><meta charset="UTF-8" /></head><body><table>${tableRows
    .map((row) => `<tr>${row}</tr>`)
    .join('')}</table></body></html>`;
  const blob = new Blob([html], {type: 'application/vnd.ms-excel;charset=utf-8'});
  const url = URL.createObjectURL(blob);
  const link = document.createElement('a');
  link.href = url;
  link.download = `tracking-events-${buildTimestamp()}.xls`;
  document.body.appendChild(link);
  link.click();
  link.remove();
  URL.revokeObjectURL(url);
}

function formatExportCell(value: unknown, key?: keyof AdminTrackingEventEntryPayload) {
  if (value === null || typeof value === 'undefined') {
    return '';
  }
  if (key === 'occurredAt') {
    return formatOccurredAt(String(value));
  }
  return String(value);
}

function formatOccurredAt(value: string) {
  const trimmed = value.trim();
  if (!trimmed) {
    return '-';
  }
  if (/^\d+$/.test(trimmed)) {
    const micros = Number(trimmed);
    if (Number.isSafeInteger(micros)) {
      return formatDateTime(new Date(Math.floor(micros / 1000)));
    }
  }
  const parsed = new Date(trimmed);
  if (!Number.isNaN(parsed.getTime())) {
    return formatDateTime(parsed);
  }
  return trimmed;
}

function formatDateTime(date: Date) {
  const pad = (value: number, size = 2) => String(value).padStart(size, '0');
  return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(
    date.getHours(),
  )}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
}

function escapeHtml(value: string) {
  return value
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;');
}

function buildTimestamp() {
  const now = new Date();
  const pad = (value: number) => String(value).padStart(2, '0');
  return [
    now.getFullYear(),
    pad(now.getMonth() + 1),
    pad(now.getDate()),
    '-',
    pad(now.getHours()),
    pad(now.getMinutes()),
    pad(now.getSeconds()),
  ].join('');
}
