import { MiniMessageComponent, MiniMessageHoverEvent, ResolvedStyle } from './mini-message.types';

/** The 16 legacy Minecraft color names, as used by NamedTextColor / MiniMessage */
const NAMED_COLORS: Record<string, string> = {
  black: '#000000',
  dark_blue: '#0000AA',
  dark_green: '#00AA00',
  dark_aqua: '#00AAAA',
  dark_red: '#AA0000',
  dark_purple: '#AA00AA',
  gold: '#FFAA00',
  gray: '#AAAAAA',
  dark_gray: '#555555',
  blue: '#5555FF',
  green: '#55FF55',
  aqua: '#55FFFF',
  red: '#FF5555',
  light_purple: '#FF55FF',
  yellow: '#FFFF55',
  white: '#FFFFFF',
};

/** Some servers map custom fonts to real web fonts; extend this as needed */
const FONT_MAP: Record<string, string> = {
  'minecraft:default': 'inherit',
  'minecraft:uniform': 'inherit',
  'minecraft:alt': "'Minecraft Enchantment', inherit",
};

export const DEFAULT_STYLE: ResolvedStyle = {
  color: undefined,
  bold: false,
  italic: false,
  underlined: false,
  strikethrough: false,
  obfuscated: false,
  font: undefined,
};

/**
 * Resolves a color field ("red" | "#RRGGBB" | "reset" | undefined) to a CSS color or
 * undefined (meaning: unset / inherit).
 */
export function resolveColor(color: string | undefined): string | undefined {
  if (!color) return undefined;
  if (color === 'reset') return undefined;
  if (color.startsWith('#')) return color;
  return NAMED_COLORS[color] ?? undefined;
}

function resolveFont(font: string | undefined): string | undefined {
  if (!font) return undefined;
  return FONT_MAP[font] ?? undefined;
}

/**
 * Merges a node's own style declarations on top of its parent's resolved style.
 * Adventure semantics: a field that is present on the node overrides the parent
 * (including explicit `false`); a field that is absent (undefined) is inherited.
 */
export function mergeStyle(parent: ResolvedStyle | undefined, node: MiniMessageComponent): ResolvedStyle {
  const base = parent ?? DEFAULT_STYLE;
  return {
    color: node.color !== undefined ? resolveColor(node.color) : base.color,
    bold: node.bold !== undefined ? node.bold : base.bold,
    italic: node.italic !== undefined ? node.italic : base.italic,
    underlined: node.underlined !== undefined ? node.underlined : base.underlined,
    strikethrough: node.strikethrough !== undefined ? node.strikethrough : base.strikethrough,
    obfuscated: node.obfuscated !== undefined ? node.obfuscated : base.obfuscated,
    font: node.font !== undefined ? resolveFont(node.font) : base.font,
  };
}

/** Converts a resolved style into an Angular [ngStyle]-compatible object */
export function styleToNgStyle(style: ResolvedStyle): Record<string, string> {
  const decorations: string[] = [];
  if (style.underlined) decorations.push('underline');
  if (style.strikethrough) decorations.push('line-through');

  return {
    color: style.color ?? 'inherit',
    'font-weight': style.bold ? 'bold' : 'normal',
    'font-style': style.italic ? 'italic' : 'normal',
    'text-decoration-line': decorations.length ? decorations.join(' ') : 'none',
    'font-family': style.font ?? 'inherit',
  };
}

/** Returns the node's own displayable text (does not recurse into `extra`) */
export function ownText(node: MiniMessageComponent): string {
  if (node.text !== undefined) return node.text;
  if (node.translate !== undefined) return node.fallback ?? `%${node.translate}%`;
  if (node.keybind !== undefined) return `[${node.keybind}]`;
  if (node.selector !== undefined) return node.selector;
  if (node.score !== undefined) return node.score.value ?? '';
  return '';
}

/** Recursively flattens a component tree (including `extra`) into plain text */
export function extractPlainText(node: MiniMessageComponent | string | undefined): string {
  if (node === undefined) return '';
  if (typeof node === 'string') return node;
  let out = ownText(node);
  if (node.extra) {
    for (const child of node.extra) {
      out += extractPlainText(child);
    }
  }
  return out;
}

/** Extracts plain text to use as a native `title` tooltip for a hoverEvent */
export function hoverEventToTitle(hover: MiniMessageHoverEvent | undefined): string | null {
  if (!hover) return null;
  if (hover.action === 'show_text') {
    const source = hover.value ?? hover.contents;
    if (typeof source === 'string') return source;
    if (source && typeof source === 'object' && ('text' in source || 'translate' in source || 'extra' in source)) {
      return extractPlainText(source as MiniMessageComponent);
    }
    return null;
  }
  if (hover.action === 'show_item') {
    const contents = hover.contents as Record<string, unknown> | undefined;
    const id = (contents?.['id'] as string) ?? (hover.value as unknown as { id?: string })?.id;
    return id ? `Item: ${id}` : 'Item';
  }
  if (hover.action === 'show_entity') {
    const contents = hover.contents as Record<string, unknown> | undefined;
    const type = (contents?.['type'] as string) ?? '';
    return type ? `Entity: ${type}` : 'Entity';
  }
  return null;
}
