import { invoke } from "@tauri-apps/api/core";

type TelemetryLevel = "info" | "warn" | "error";

const MAX_MESSAGE_LENGTH = 2_048;
let telemetryEnabled = false;
let configuration: Promise<unknown> = Promise.resolve();
let frontendShuttingDown = false;

export function beginFrontendShutdown() {
  frontendShuttingDown = true;
}

// Telemetry must never contain repository contents or identifying local data.
function sanitize(message: string): string {
  return message
    .replace(/https?:\/\/\S+/gi, "[url]")
    .replace(/(?:[A-Za-z]:\\|\/)(?:[^\s<>:"|?*]+[\\/])*[^\s<>:"|?*]*/g, "[path]")
    .replace(/(token|password|secret|authorization|credential)\s*[:=]\s*\S+/gi, "$1=[redacted]")
    .replace(/[\w.+-]+@[\w.-]+\.[A-Za-z]{2,}/g, "[email]")
    .slice(0, MAX_MESSAGE_LENGTH);
}

export function setTelemetryEnabled(enabled: boolean) {
  telemetryEnabled = enabled;
  configuration = invoke("set_telemetry_enabled", { enabled }).catch(() => {});
}

export function telemetryLog(level: TelemetryLevel, message: string, eventName?: string) {
  if (!telemetryEnabled) return;
  void configuration.then(() => invoke("emit_frontend_log", {
      log: {
        level,
        message: sanitize(message),
        eventName,
      },
    }))
    .catch(() => {
      // Observability must never interrupt the Git workflow.
    });
}

function randomHex(bytes: number): string {
  const values = new Uint8Array(bytes);
  crypto.getRandomValues(values);
  return Array.from(values, (value) => value.toString(16).padStart(2, "0")).join("");
}

export async function tracedInvoke<T>(command: string, args?: Record<string, unknown>): Promise<T> {
  if (frontendShuttingDown) {
    // The window is on its way out and nothing calls preventDefault() on the
    // close event, so it will finish closing regardless. Never resolving
    // avoids surfacing a spurious "shutting down" error in whatever UI catch
    // block happens to be awaiting an in-flight operation (e.g. a pull/push
    // that makes several sequential calls) when the close request lands.
    return new Promise<T>(() => {});
  }
  if (!telemetryEnabled) return invoke<T>(command, args);
  const startedAtMs = Date.now();
  const started = performance.now();
  const traceId = randomHex(16);
  const spanId = randomHex(8);
  try {
    const result = await invoke<T>(command, args);
    void configuration.then(() => invoke("emit_frontend_span", {
      span: { name: command, traceId, spanId, startedAtMs, durationMs: performance.now() - started, success: true },
    })).catch(() => {});
    return result;
  } catch (error) {
    void configuration.then(() => invoke("emit_frontend_span", {
      span: { name: command, traceId, spanId, startedAtMs, durationMs: performance.now() - started, success: false },
    })).catch(() => {});
    throw error;
  }
}

export function installGlobalErrorTelemetry() {
  window.addEventListener("error", (event) => {
    telemetryLog("error", event.message || "Unhandled frontend error", "frontend.unhandled_error");
  });
  window.addEventListener("unhandledrejection", (event) => {
    const message = event.reason instanceof Error ? event.reason.message : String(event.reason ?? "Unhandled promise rejection");
    telemetryLog("error", message, "frontend.unhandled_rejection");
  });
}
