import { z } from 'zod';
import { appEnv } from '@/config/env';

export class ApiError extends Error {
  constructor(
    public status: number,
    message: string,
  ) {
    super(message);
    this.name = 'ApiError';
  }
}

/**
 * Thrown when a free-plan network attempts to create a non-container particle
 * after hitting its daily message limit. Compose UI also disables triggers
 * proactively via `useNetworkUsage` — this throw is a last-line defense.
 */
export class QuotaExceededError extends Error {
  constructor(public readonly networkId: string) {
    super('Daily message limit reached');
    this.name = 'QuotaExceededError';
  }
}

const IPC_PREFIX = /^Error invoking remote method '[^']+':\s*/;

function normalizeMessage(message: string): string {
  return message
    .replace(IPC_PREFIX, '')
    .replace(/^Error:\s*/, '')
    .trim();
}

export function toUserMessage(err: unknown): string {
  if (err instanceof ApiError) {
    if (err.status === 401) return 'Please sign in again.';
    if (err.status === 403) return "You don't have permission to do that.";
    if (err.status === 404) return 'Not found.';
    if (err.status === 408 || err.status === 429) {
      return 'Please try again in a moment.';
    }
    if (err.status >= 500) {
      return 'Something went wrong on our end. Please try again.';
    }
    return normalizeMessage(err.message) || 'Request failed.';
  }

  if (err instanceof z.ZodError) {
    return 'Received unexpected data from the server.';
  }

  if (err instanceof TypeError && /fetch|network/i.test(err.message)) {
    return 'Network error. Check your connection.';
  }

  if (err instanceof Error) {
    return normalizeMessage(err.message) || 'Something went wrong.';
  }

  return 'Something went wrong.';
}

type ErrorContext = Record<string, unknown>;
type ErrorSink = (err: unknown, context?: ErrorContext) => void;

// Sentry (or any observability backend) installs itself via `installErrorSinks`
// from renderer.tsx / main.ts. Until then, logError is a dev-only console call
// and reportError always prints — no call site needs to know.
let captureSink: ErrorSink | null = null;
let breadcrumbSink: ErrorSink | null = null;

export function installErrorSinks(sinks: {
  capture: ErrorSink;
  breadcrumb: ErrorSink;
}): void {
  captureSink = sinks.capture;
  breadcrumbSink = sinks.breadcrumb;
}

/** Expected-but-recordable failures. Breadcrumb only — never pages anyone. */
export function logError(err: unknown, context?: ErrorContext): void {
  if (appEnv === 'dev') {
    // eslint-disable-next-line no-console
    console.error('[error]', err, context ?? {});
  }
  breadcrumbSink?.(err, context);
}

/** Unexpected failures the user may not see. Always captured. */
export function reportError(err: unknown, context?: ErrorContext): void {
  // eslint-disable-next-line no-console
  console.error('[error]', err, context ?? {});
  captureSink?.(err, context);
}
