import {
  Interaction,
  EmbedBuilder,
  ChatInputCommandInteraction,
  MessageComponentInteraction,
  ModalSubmitInteraction,
} from 'discord.js';
import { BotError, ErrorCategory } from './BotError';
import { ErrorDefs, createBotError } from './ErrorCodes';
import { logger } from '../utils/logger';
import { t, SupportedLocale, DEFAULT_LOCALE } from '../i18n';

/** Embed color per error category */
const CATEGORY_COLORS: Record<ErrorCategory, number> = {
  [ErrorCategory.USER_INPUT]: 0xffa500,     // Orange
  [ErrorCategory.PERMISSION]: 0xff6b6b,     // Red
  [ErrorCategory.BOT_INTERNAL]: 0xff4444,    // Dark Red
  [ErrorCategory.DISCORD_API]: 0x7289da,     // Discord Blurple
};

/** Emoji per error category */
const CATEGORY_EMOJI: Record<ErrorCategory, string> = {
  [ErrorCategory.USER_INPUT]: '⚠️',
  [ErrorCategory.PERMISSION]: '🔒',
  [ErrorCategory.BOT_INTERNAL]: '❌',
  [ErrorCategory.DISCORD_API]: '🔄',
};

export type RepliableInteraction =
  | ChatInputCommandInteraction
  | MessageComponentInteraction
  | ModalSubmitInteraction;

/**
 * Utility class for reporting errors as user-friendly, localized Embeds.
 * Error codes and stack traces are logged server-side only.
 */
export class ErrorReporter {
  /**
   * Reports a BotError to the user via an ephemeral Embed,
   * and logs the full error details to the server console.
   */
  static async report(
    interaction: RepliableInteraction,
    error: BotError,
    locale: SupportedLocale = DEFAULT_LOCALE,
  ): Promise<void> {
    // ── Server-side logging (detailed) ──
    logger.error(
      `${error.code}: ${error.message}`,
      error.cause ? error.cause : '',
    );

    // ── User-facing Embed (friendly, localized, no error codes) ──
    const embed = this.buildEmbed(error, locale);

    try {
      if (interaction.replied || interaction.deferred) {
        await interaction.followUp({ embeds: [embed], ephemeral: true });
      } else {
        await interaction.reply({ embeds: [embed], ephemeral: true });
      }
    } catch (replyError) {
      // If even the error reply fails, log it silently
      logger.error('ErrorReporter: Failed to send error response to user', replyError);
    }
  }

  /**
   * Wraps an unknown error into a structured BotError.
   * Automatically maps known Discord API error codes.
   */
  static wrap(error: unknown): BotError {
    // Already a BotError — return as-is
    if (error instanceof BotError) {
      return error;
    }

    const err = error instanceof Error ? error : new Error(String(error));

    // Map known Discord API error codes
    const discordCode = (error as any)?.code;
    if (typeof discordCode === 'number') {
      switch (discordCode) {
        case 50013:
          return createBotError(ErrorDefs.DISCORD_MISSING_PERMISSIONS, err);
        case 50001: // Missing Access
          return createBotError(ErrorDefs.BOT_MISSING_MANAGE_CHANNELS, err);
        default:
          break;
      }
    }

    // Map rate limit errors
    if ((error as any)?.httpStatus === 429) {
      return createBotError(ErrorDefs.DISCORD_RATE_LIMITED, err);
    }

    // Fallback — unknown internal error
    return createBotError(ErrorDefs.UNKNOWN_ERROR, err);
  }

  /**
   * Builds a user-friendly, localized Embed from a BotError.
   * Does NOT include error codes — those stay in logs only.
   */
  private static buildEmbed(error: BotError, locale: SupportedLocale): EmbedBuilder {
    const emoji = CATEGORY_EMOJI[error.category];
    const title = t(locale, `errorTitles.${error.category}`);
    const userMessage = t(locale, `${error.messageKey}.userMessage`);
    const resolution = t(locale, `${error.messageKey}.resolution`);
    const fieldName = t(locale, 'errorFields.resolution');

    const embed = new EmbedBuilder()
      .setColor(CATEGORY_COLORS[error.category])
      .setTitle(`${emoji} ${title}`)
      .setDescription(userMessage)
      .setTimestamp();

    // Only add resolution field if the translation exists (not the key itself)
    if (resolution && resolution !== `${error.messageKey}.resolution`) {
      embed.addFields({
        name: fieldName,
        value: resolution,
      });
    }

    return embed;
  }
}

/**
 * Wraps an async handler function with standardized, localized error handling.
 * Any thrown BotError is reported to the user; unknown errors are wrapped first.
 */
export async function withErrorHandler(
  interaction: RepliableInteraction,
  fn: () => Promise<void>,
  locale: SupportedLocale = DEFAULT_LOCALE,
): Promise<void> {
  try {
    await fn();
  } catch (error) {
    const botError = ErrorReporter.wrap(error);
    await ErrorReporter.report(interaction, botError, locale);
  }
}
