/**
 * Sentry Error Tracking — Optional Integration
 *
 * Activated only when SENTRY_DSN environment variable is set.
 * Uses the Sentry REST API directly (no SDK dependency required).
 *
 * For full SDK integration, install @sentry/node and replace captureException
 * with the SDK's init() + Sentry.captureException().
 *
 * Usage:
 *   import { captureException, captureMessage } from '@/utils/sentry';
 *   captureException(error, { scope: 'api.contact', extra: { ip } });
 *   captureMessage('Rate limit hit', 'warning', { scope: 'api.contact' });
 */

import { logger } from './logger';

interface SentryContext {
  scope?: string;         // e.g. 'api.contact'
  user?: { ip?: string; email?: string };
  extra?: Record<string, unknown>;
  tags?: Record<string, string>;
}

interface SentryEnvelope {
  event_id: string;
  timestamp: string;
  platform: string;
  level: string;
  environment: string;
  release?: string;
  transaction?: string;
  logger?: string;
  exception?: {
    values: Array<{
      type: string;
      value: string;
      stacktrace?: { frames: Array<{ filename?: string; function?: string; lineno?: number }> };
    }>;
  };
  message?: string;
  user?: { ip_address?: string; email?: string };
  extra?: Record<string, unknown>;
  tags?: Record<string, string>;
}

function getSentryDsn(): string | undefined {
  try {
    return (import.meta as { env?: { SENTRY_DSN?: string } }).env?.SENTRY_DSN;
  } catch {
    return process.env.SENTRY_DSN;
  }
}

function getEnvironment(): string {
  try {
    return (import.meta as { env?: { PROD?: boolean } }).env?.PROD ? 'production' : 'development';
  } catch {
    return process.env.NODE_ENV ?? 'development';
  }
}

function getRelease(): string | undefined {
  try {
    return process.env.npm_package_version ?? process.env.RELEASE_VERSION;
  } catch {
    return undefined;
  }
}

function generateEventId(): string {
  return Array.from({ length: 32 }, () => Math.floor(Math.random() * 16).toString(16)).join('');
}

/** Parse DSN into project URL and auth header */
function parseDsn(dsn: string): { url: string; auth: string } | null {
  try {
    const url = new URL(dsn);
    const key = url.username;
    const projectId = url.pathname.replace('/', '');
    const storeUrl = `${url.protocol}//${url.host}/api/${projectId}/store/`;
    return { url: storeUrl, auth: `Sentry sentry_version=7, sentry_key=${key}` };
  } catch {
    return null;
  }
}

async function sendToSentry(payload: SentryEnvelope): Promise<void> {
  const dsn = getSentryDsn();
  if (!dsn) return;

  const parsed = parseDsn(dsn);
  if (!parsed) {
    logger.warn('sentry', 'Invalid SENTRY_DSN format — skipping event');
    return;
  }

  try {
    const res = await fetch(parsed.url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-Sentry-Auth': parsed.auth,
      },
      body: JSON.stringify(payload),
    });

    if (!res.ok) {
      logger.warn('sentry', 'Failed to send event to Sentry', { status: res.status });
    }
  } catch (err) {
    // Never let Sentry reporting crash the application
    logger.warn('sentry', 'Network error sending to Sentry', {
      error: err instanceof Error ? err.message : String(err),
    });
  }
}

/**
 * Capture an exception and send to Sentry (if configured).
 * Also logs the error via the structured logger.
 */
export async function captureException(
  error: unknown,
  context: SentryContext = {}
): Promise<void> {
  const err = error instanceof Error ? error : new Error(String(error));
  const scope = context.scope ?? 'app';

  // Always log locally
  logger.error(scope, err.message, {
    error: err.name,
    stack: err.stack?.split('\n').slice(0, 5).join(' | '),
    ...context.extra,
  });

  // Send to Sentry if DSN is configured
  const dsn = getSentryDsn();
  if (!dsn) return;

  const frames = err.stack
    ?.split('\n')
    .slice(1)
    .map((line) => {
      const match = line.trim().match(/at\s+(.+?)\s+\((.+?):(\d+):\d+\)/) ??
                    line.trim().match(/at\s+(.+?):(\d+):\d+/);
      if (match) {
        return {
          function: match[1],
          filename: match[2] ?? match[1],
          lineno: parseInt(match[3] ?? match[2], 10),
        };
      }
      return { filename: line.trim() };
    }) ?? [];

  await sendToSentry({
    event_id: generateEventId(),
    timestamp: new Date().toISOString(),
    platform: 'node',
    level: 'error',
    environment: getEnvironment(),
    release: getRelease(),
    logger: scope,
    transaction: scope,
    exception: {
      values: [{
        type: err.name,
        value: err.message,
        stacktrace: { frames },
      }],
    },
    user: context.user
      ? { ip_address: context.user.ip, email: context.user.email }
      : undefined,
    extra: context.extra,
    tags: context.tags,
  });
}

/**
 * Capture a message (non-exception) and send to Sentry (if configured).
 * Also logs the message via the structured logger.
 */
export async function captureMessage(
  message: string,
  level: 'info' | 'warning' | 'error' = 'info',
  context: SentryContext = {}
): Promise<void> {
  const scope = context.scope ?? 'app';

  if (level === 'error') {
    logger.error(scope, message, context.extra);
  } else if (level === 'warning') {
    logger.warn(scope, message, context.extra);
  } else {
    logger.info(scope, message, context.extra);
  }

  const dsn = getSentryDsn();
  if (!dsn) return;

  await sendToSentry({
    event_id: generateEventId(),
    timestamp: new Date().toISOString(),
    platform: 'node',
    level: level === 'warning' ? 'warning' : level,
    environment: getEnvironment(),
    release: getRelease(),
    logger: scope,
    message,
    user: context.user
      ? { ip_address: context.user.ip, email: context.user.email }
      : undefined,
    extra: context.extra,
    tags: context.tags,
  });
}
