import { appendLogLine, resolveDefaultLogFilePath } from '../shared/log-files';

export type FatalErrorReportOptions = {
  title: string;
  context: string;
};

export type FatalErrorReporterDeps = {
  showErrorBox: (title: string, details: string) => void;
  consoleError?: (message: string, error?: unknown) => void;
  appendLogLine?: (line: string) => void;
  resolveLogFilePath?: () => string;
  now?: () => Date;
};

export type FatalErrorReporter = (
  error: unknown,
  options?: Partial<FatalErrorReportOptions>,
) => void;

const DEFAULT_TITLE = 'SubMiner crashed';
const DEFAULT_CONTEXT = 'SubMiner encountered a fatal error';

let fatalErrorReported = false;

function pad(value: number): string {
  return String(value).padStart(2, '0');
}

function formatTimestamp(date: Date): string {
  return [
    date.getFullYear(),
    '-',
    pad(date.getMonth() + 1),
    '-',
    pad(date.getDate()),
    ' ',
    pad(date.getHours()),
    ':',
    pad(date.getMinutes()),
    ':',
    pad(date.getSeconds()),
  ].join('');
}

function stringifyUnknownError(error: unknown): string {
  if (error instanceof Error) {
    return error.stack || error.message || error.name;
  }
  if (typeof error === 'string') {
    return error;
  }
  try {
    return JSON.stringify(error) ?? String(error);
  } catch {
    return String(error);
  }
}

export function buildFatalErrorDetails(options: {
  context: string;
  error: unknown;
  logFilePath: string;
}): string {
  return [
    options.context,
    '',
    stringifyUnknownError(options.error),
    '',
    `Log file: ${options.logFilePath}`,
  ].join('\n');
}

export function createFatalErrorReporter(deps: FatalErrorReporterDeps): FatalErrorReporter {
  return (error, options = {}) => {
    if (fatalErrorReported) {
      return;
    }
    fatalErrorReported = true;

    const title = options.title ?? DEFAULT_TITLE;
    const context = options.context ?? DEFAULT_CONTEXT;
    const logFilePath = deps.resolveLogFilePath?.() ?? resolveDefaultLogFilePath('app');
    const details = buildFatalErrorDetails({ context, error, logFilePath });
    const timestamp = formatTimestamp(deps.now?.() ?? new Date());
    const line = `[subminer] - ${timestamp} - ERROR - [main:fatal] ${details.replace(/\r?\n/g, ' | ')}`;

    try {
      (deps.appendLogLine ?? ((entry: string) => appendLogLine(logFilePath, entry)))(line);
    } catch {
      // Fatal reporting must never throw while handling the original failure.
    }

    try {
      deps.consoleError?.(line, error);
    } catch {
      // ignore console sink failures
    }

    try {
      deps.showErrorBox(title, details);
    } catch {
      // If native dialogs are unavailable, the file log above is still the source of truth.
    }
  };
}

export function registerFatalErrorHandlers(deps: {
  reportFatalError: FatalErrorReporter;
  exit: (code: number) => void;
}): void {
  process.on('uncaughtException', (error) => {
    deps.reportFatalError(error, {
      title: 'SubMiner crashed',
      context: 'SubMiner main process threw an uncaught exception.',
    });
    deps.exit(1);
  });

  process.on('unhandledRejection', (reason) => {
    deps.reportFatalError(reason, {
      title: 'SubMiner crashed',
      context: 'SubMiner main process had an unhandled promise rejection.',
    });
    deps.exit(1);
  });
}

export function resetFatalErrorReporterForTests(): void {
  fatalErrorReported = false;
}
