export type Asuka3DPerfLevel = 'info' | 'warn' | 'error';

export type Asuka3DPerfEntry = {
  seq: number;
  at: string;
  t: number;
  category: string;
  phase: string;
  durationMs?: number;
  level: Asuka3DPerfLevel;
  payload: Record<string, unknown>;
};

type Asuka3DPerfApi = {
  enable: () => void;
  disable: () => void;
  clear: () => void;
  getLogs: () => Asuka3DPerfEntry[];
  export: () => string;
  summary: () => Array<{ key: string; count: number; totalMs: number; avgMs: number; maxMs: number }>;
  download: (filename?: string) => void;
  copy: () => Promise<boolean>;
  status: () => { enabled: boolean; entries: number };
};

const PERF_PREFIX = '[Asuka3D:Perf]';
const STORAGE_KEY = 'asuka:3d-perf-debug';
const MAX_ENTRIES = 4000;

const perfNow = (): number => {
  try { return typeof performance !== 'undefined' && typeof performance.now === 'function' ? performance.now() : Date.now(); }
  catch { return Date.now(); }
};

const summarize = (value: unknown, depth = 0): unknown => {
  if (value == null) return value;
  if (value instanceof Error) return { name: value.name, message: value.message, stack: value.stack };
  if (typeof value === 'string') {
    if (/^data:/i.test(value)) return { kind: 'data-url', length: value.length, prefix: value.slice(0, 80) };
    if (/^blob:/i.test(value)) return { kind: 'blob-url', length: value.length, prefix: value.slice(0, 120) };
    return value.length > 500 ? `${value.slice(0, 240)}… (${value.length} chars)` : value;
  }
  if (typeof value !== 'object') return value;
  try {
    if (typeof Blob !== 'undefined' && value instanceof Blob) return { kind: 'blob', size: value.size, type: value.type };
    if (typeof File !== 'undefined' && value instanceof File) return { kind: 'file', name: value.name, size: value.size, type: value.type };
  } catch {}
  if (Array.isArray(value)) {
    const max = depth > 1 ? 20 : 50;
    const next = value.slice(0, max).map((item) => summarize(item, depth + 1));
    if (value.length > max) next.push(`… ${value.length - max} more`);
    return next;
  }
  if (depth >= 4) return '[Object]';
  const output: Record<string, unknown> = {};
  const input = value as Record<string, unknown>;
  const keys = Object.keys(input);
  for (const key of keys.slice(0, 60)) output[key] = summarize(input[key], depth + 1);
  if (keys.length > 60) output.__remainingKeys = keys.length - 60;
  return output;
};

const getGlobal = (): any => globalThis as any;

export const isAsuka3DPerfDebugEnabled = (): boolean => {
  try {
    const g = getGlobal();
    if (g.__ASUKA_3D_PERF_ENABLED__ === true) return true;
    if (g.__ASUKA_3D_PERF_ENABLED__ === false) return false;
    const search = typeof window !== 'undefined' ? String(window.location?.search ?? '') : '';
    if (/(?:^|[?&])asuka3dperf=1(?:&|$)/i.test(search)) return true;
    if (/(?:^|[?&])asuka3dperf=0(?:&|$)/i.test(search)) return false;
    const stored = typeof window !== 'undefined' ? window.localStorage?.getItem(STORAGE_KEY) : null;
    return stored === '1' || stored === 'true' || stored === 'on';
  } catch {
    return false;
  }
};

const getEntries = (): Asuka3DPerfEntry[] => {
  const g = getGlobal();
  if (!Array.isArray(g.__ASUKA_3D_PERF_LOGS__)) g.__ASUKA_3D_PERF_LOGS__ = [];
  return g.__ASUKA_3D_PERF_LOGS__ as Asuka3DPerfEntry[];
};

export const logAsuka3DPerf = (
  category: string,
  phase: string,
  payload: Record<string, unknown> = {},
  options: { durationMs?: number; level?: Asuka3DPerfLevel; force?: boolean; console?: boolean } = {},
): Asuka3DPerfEntry | null => {
  if (!options.force && !isAsuka3DPerfDebugEnabled()) return null;
  const g = getGlobal();
  const entries = getEntries();
  const seq = Number(g.__ASUKA_3D_PERF_SEQ__ ?? 0) + 1;
  g.__ASUKA_3D_PERF_SEQ__ = seq;
  const entry: Asuka3DPerfEntry = {
    seq,
    at: new Date().toISOString(),
    t: Math.round(perfNow() * 100) / 100,
    category: String(category || 'general'),
    phase: String(phase || 'mark'),
    durationMs: Number.isFinite(Number(options.durationMs)) ? Math.round(Number(options.durationMs) * 100) / 100 : undefined,
    level: options.level ?? 'info',
    payload: summarize(payload) as Record<string, unknown>,
  };
  entries.push(entry);
  if (entries.length > MAX_ENTRIES) entries.splice(0, entries.length - MAX_ENTRIES);
  if (false && options.console !== false) {
    const label = `${PERF_PREFIX} ${entry.category}.${entry.phase}${entry.durationMs != null ? ` ${entry.durationMs}ms` : ''}`;
    const logger = entry.level === 'error' ? console.error : entry.level === 'warn' ? console.warn : console.info;
    try { logger(label, entry.payload); } catch {}
  }
  return entry;
};

export const beginAsuka3DPerfSpan = (
  category: string,
  phase: string,
  payload: Record<string, unknown> = {},
): ((endPayload?: Record<string, unknown>, options?: { level?: Asuka3DPerfLevel; console?: boolean }) => number) => {
  const enabled = isAsuka3DPerfDebugEnabled();
  const startedAt = perfNow();
  if (enabled) logAsuka3DPerf(category, `${phase}:start`, payload, { console: true });
  return (endPayload = {}, options = {}) => {
    const durationMs = perfNow() - startedAt;
    if (enabled) logAsuka3DPerf(category, `${phase}:end`, { ...payload, ...endPayload }, { durationMs, level: options.level, console: options.console });
    return durationMs;
  };
};

export const measureAsuka3DPerf = async <T>(
  category: string,
  phase: string,
  task: () => Promise<T> | T,
  payload: Record<string, unknown> = {},
): Promise<T> => {
  const end = beginAsuka3DPerfSpan(category, phase, payload);
  try {
    const result = await task();
    end({ ok: true });
    return result;
  } catch (error) {
    end({ ok: false, error: summarize(error) as any }, { level: 'error' });
    throw error;
  }
};

const makeSummary = (): Array<{ key: string; count: number; totalMs: number; avgMs: number; maxMs: number }> => {
  const grouped = new Map<string, { count: number; totalMs: number; maxMs: number }>();
  for (const entry of getEntries()) {
    if (entry.durationMs == null) continue;
    const key = `${entry.category}.${entry.phase}`;
    const current = grouped.get(key) ?? { count: 0, totalMs: 0, maxMs: 0 };
    current.count += 1;
    current.totalMs += entry.durationMs;
    current.maxMs = Math.max(current.maxMs, entry.durationMs);
    grouped.set(key, current);
  }
  return Array.from(grouped.entries())
    .map(([key, value]) => ({ key, count: value.count, totalMs: Math.round(value.totalMs * 100) / 100, avgMs: Math.round((value.totalMs / value.count) * 100) / 100, maxMs: Math.round(value.maxMs * 100) / 100 }))
    .sort((a, b) => b.totalMs - a.totalMs);
};

export const ensureAsuka3DPerfDebugApi = (): Asuka3DPerfApi => {
  const g = getGlobal();
  if (g.__ASUKA_3D_PERF__) return g.__ASUKA_3D_PERF__ as Asuka3DPerfApi;
  const api: Asuka3DPerfApi = {
    enable: () => {
      g.__ASUKA_3D_PERF_ENABLED__ = true;
      try { window.localStorage?.setItem(STORAGE_KEY, '1'); } catch {}
      logAsuka3DPerf('debug', 'enabled', { reloadRecommended: true }, { force: true });
    },
    disable: () => {
      logAsuka3DPerf('debug', 'disabled', {}, { force: true });
      g.__ASUKA_3D_PERF_ENABLED__ = false;
      try { window.localStorage?.removeItem(STORAGE_KEY); } catch {}
    },
    clear: () => {
      g.__ASUKA_3D_PERF_LOGS__ = [];
      g.__ASUKA_3D_PERF_SEQ__ = 0;
      console.info(`${PERF_PREFIX} logs cleared`);
    },
    getLogs: () => getEntries().map((entry) => ({ ...entry, payload: { ...entry.payload } })),
    export: () => JSON.stringify({
      generatedAt: new Date().toISOString(),
      location: typeof window !== 'undefined' ? window.location?.href : undefined,
      userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : undefined,
      status: { enabled: isAsuka3DPerfDebugEnabled(), entries: getEntries().length },
      summary: makeSummary(),
      logs: getEntries(),
    }, null, 2),
    summary: makeSummary,
    download: (filename = `asuka-3d-perf-${new Date().toISOString().replace(/[:.]/g, '-')}.json`) => {
      if (typeof document === 'undefined') return;
      const blob = new Blob([api.export()], { type: 'application/json' });
      const url = URL.createObjectURL(blob);
      const anchor = document.createElement('a');
      anchor.href = url;
      anchor.download = filename;
      anchor.click();
      setTimeout(() => URL.revokeObjectURL(url), 1000);
    },
    copy: async () => {
      try {
        await navigator.clipboard.writeText(api.export());
        console.info(`${PERF_PREFIX} copied ${getEntries().length} entries`);
        return true;
      } catch (error) {
        console.error(`${PERF_PREFIX} copy failed`, error);
        return false;
      }
    },
    status: () => ({ enabled: isAsuka3DPerfDebugEnabled(), entries: getEntries().length }),
  };
  g.__ASUKA_3D_PERF__ = api;
  return api;
};

ensureAsuka3DPerfDebugApi();
