import type {
  LogLevel,
  LogRecord,
  TextFormatter,
  TextFormatterOptions,
} from "@logtape/logtape";
import { inspect as nodeInspect } from "node:util";
import { getOptimalWordWrapWidth } from "./terminal.ts";
import { truncateCategory, type TruncationStrategy } from "./truncate.ts";
import { getDisplayWidth } from "./wcwidth.ts";
import { wrapText } from "./wordwrap.ts";

/**
 * ANSI escape codes for styling
 */
const RESET = "\x1b[0m";
const DIM = "\x1b[2m";

// Default true color values (referenced in JSDoc)
const defaultColors = {
  trace: "rgb(167,139,250)", // Light purple
  debug: "rgb(96,165,250)", // Light blue
  info: "rgb(52,211,153)", // Emerald
  warning: "rgb(251,191,36)", // Amber
  error: "rgb(248,113,113)", // Light red
  fatal: "rgb(220,38,38)", // Dark red
  category: "rgb(100,116,139)", // Slate
  message: "rgb(148,163,184)", // Light slate
  timestamp: "rgb(100,116,139)", // Slate
} as const;

/**
 * ANSI style codes
 */
const styles = {
  reset: RESET,
  bold: "\x1b[1m",
  dim: DIM,
  italic: "\x1b[3m",
  underline: "\x1b[4m",
  strikethrough: "\x1b[9m",
} as const;

/**
 * Standard ANSI colors (16-color)
 */
const ansiColors = {
  black: "\x1b[30m",
  red: "\x1b[31m",
  green: "\x1b[32m",
  yellow: "\x1b[33m",
  blue: "\x1b[34m",
  magenta: "\x1b[35m",
  cyan: "\x1b[36m",
  white: "\x1b[37m",
} as const;

/**
 * Color type definition
 */
export type Color =
  | keyof typeof ansiColors
  | `rgb(${number},${number},${number})`
  | `#${string}`
  | null;

/**
 * Category color mapping for prefix-based coloring.
 *
 * Maps category prefixes (as arrays) to colors. The formatter will match
 * categories against these prefixes and use the corresponding color.
 * Longer/more specific prefixes take precedence over shorter ones.
 *
 * @example
 * ```typescript
 * new Map([
 *   [["app", "auth"], "#ff6b6b"],     // app.auth.* -> red
 *   [["app", "db"], "#4ecdc4"],       // app.db.* -> teal
 *   [["app"], "#45b7d1"],             // app.* (fallback) -> blue
 *   [["lib"], "#96ceb4"],             // lib.* -> green
 * ])
 * ```
 */
export type CategoryColorMap = Map<readonly string[], Color>;

/**
 * Internal representation of category prefix patterns
 */
type CategoryPattern = {
  prefix: readonly string[];
  color: Color;
};

/**
 * Style type definition - supports single styles, arrays of styles, or null
 */
export type Style = keyof typeof styles | (keyof typeof styles)[] | null;

// Pre-compiled regex patterns for color parsing
const RGB_PATTERN = /^rgb\((\d+),(\d+),(\d+)\)$/;
const HEX_PATTERN = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;

/**
 * Helper function to convert color to ANSI escape code
 */
function colorToAnsi(color: Color): string {
  if (color === null) return "";
  if (color in ansiColors) {
    return ansiColors[color as keyof typeof ansiColors];
  }

  // Handle rgb() format
  const rgbMatch = color.match(RGB_PATTERN);
  if (rgbMatch) {
    const [, r, g, b] = rgbMatch;
    return `\x1b[38;2;${r};${g};${b}m`;
  }

  // Handle hex format (#rrggbb or #rgb)
  const hexMatch = color.match(HEX_PATTERN);
  if (hexMatch) {
    let hex = hexMatch[1];
    // Convert 3-digit hex to 6-digit
    if (hex.length === 3) {
      hex = hex.split("").map((c) => c + c).join("");
    }
    const r = parseInt(hex.substr(0, 2), 16);
    const g = parseInt(hex.substr(2, 2), 16);
    const b = parseInt(hex.substr(4, 2), 16);
    return `\x1b[38;2;${r};${g};${b}m`;
  }

  return "";
}

/**
 * Helper function to convert style to ANSI escape code
 */
function styleToAnsi(style: Style): string {
  if (style === null) return "";
  if (Array.isArray(style)) {
    return style.map((s) => styles[s] || "").join("");
  }
  return styles[style] || "";
}

/**
 * Converts a category color map to internal patterns and sorts them by specificity.
 * More specific (longer) prefixes come first for proper matching precedence.
 */
function prepareCategoryPatterns(
  categoryColorMap: CategoryColorMap,
): CategoryPattern[] {
  const patterns: CategoryPattern[] = [];

  for (const [prefix, color] of categoryColorMap) {
    patterns.push({ prefix, color });
  }

  // Sort by prefix length (descending) for most-specific-first matching
  return patterns.sort((a, b) => b.prefix.length - a.prefix.length);
}

/**
 * Matches a category against category color patterns.
 * Returns the color of the first matching pattern, or null if no match.
 */
function matchCategoryColor(
  category: readonly string[],
  patterns: CategoryPattern[],
): Color {
  for (const pattern of patterns) {
    if (categoryMatches(category, pattern.prefix)) {
      return pattern.color;
    }
  }
  return null;
}

/**
 * Checks if a category matches a prefix pattern.
 * A category matches if it starts with all segments of the prefix.
 */
function categoryMatches(
  category: readonly string[],
  prefix: readonly string[],
): boolean {
  if (prefix.length > category.length) {
    return false;
  }

  for (let i = 0; i < prefix.length; i++) {
    if (category[i] !== prefix[i]) {
      return false;
    }
  }

  return true;
}

/**
 * Default icons for each log level
 */
const defaultIcons: Record<LogLevel, string> = {
  trace: "🔍",
  debug: "🐛",
  info: "✨",
  warning: "⚡",
  error: "❌",
  fatal: "💀",
};

/**
 * Normalize icon spacing to ensure consistent column alignment.
 *
 * All icons will be padded with spaces to match the width of the widest icon,
 * ensuring consistent prefix alignment across all log levels.
 *
 * @param iconMap The icon mapping to normalize
 * @returns A new icon map with consistent spacing
 */
function normalizeIconSpacing(
  iconMap: Record<LogLevel, string>,
): Record<LogLevel, string> {
  const entries = Object.entries(iconMap) as Array<[LogLevel, string]>;
  const maxWidth = Math.max(
    ...entries.map(([, icon]) => getDisplayWidth(icon)),
  );

  return Object.fromEntries(
    entries.map(([level, icon]) => [
      level,
      icon + " ".repeat(maxWidth - getDisplayWidth(icon)),
    ]),
  ) as Record<LogLevel, string>;
}

/**
 * Platform-specific inspect function. Uses Node.js `util.inspect()` which
 * is available in both Deno and Node.js environments. For browser environments,
 * it falls back to {@link JSON.stringify}.
 *
 * @param value The value to inspect.
 * @param options The options for inspecting the value.
 * @returns The string representation of the value.
 */
const inspect: (value: unknown, options?: { colors?: boolean }) => string =
  // @ts-ignore: Browser detection
  typeof document !== "undefined" ||
    // @ts-ignore: React Native detection
    typeof navigator !== "undefined" && navigator.product === "ReactNative"
    ? (v) => JSON.stringify(v)
    : (v, opts) =>
      nodeInspect(v, {
        maxArrayLength: 10,
        maxStringLength: 80,
        compact: true,
        ...opts,
      });

/**
 * Configuration options for the pretty formatter.
 *
 * This interface extends the base text formatter options while providing
 * extensive customization options for visual styling, layout control, and
 * development-focused features. It offers granular control over colors,
 * styles, and formatting similar to the ANSI color formatter.
 *
 * @since 1.0.0
 */
export interface PrettyFormatterOptions
  extends Omit<TextFormatterOptions, "category" | "value" | "format"> {
  /**
   * Color for timestamp display when timestamps are enabled.
   *
   * Supports true color RGB values, hex colors, or ANSI color names.
   * Set to `null` to disable timestamp coloring.
   *
   * @example
   * ```typescript
   * timestampColor: "#888888"        // Hex color
   * timestampColor: "rgb(128,128,128)" // RGB color
   * timestampColor: "cyan"           // ANSI color name
   * timestampColor: null             // No color
   * ```
   *
   * @default `"rgb(100,116,139)"` (slate gray)
   */
  readonly timestampColor?: Color;

  /**
   * Visual style applied to timestamp text.
   *
   * Controls text appearance like boldness, dimming, etc.
   * Supports single styles, multiple styles combined, or no styling.
   * Combines with `timestampColor` for full styling control.
   *
   * @example
   * ```typescript
   * timestampStyle: "dim"                    // Single style: dimmed text
   * timestampStyle: "bold"                   // Single style: bold text
   * timestampStyle: ["bold", "underline"]    // Multiple styles: bold + underlined
   * timestampStyle: ["dim", "italic"]        // Multiple styles: dimmed + italic
   * timestampStyle: null                     // No styling
   * ```
   *
   * @default `"dim"`
   */
  readonly timestampStyle?: Style;

  /**
   * Custom colors for each log level.
   *
   * Allows fine-grained control over level appearance. Each level can have
   * its own color scheme. Unspecified levels use built-in defaults.
   * Set individual levels to `null` to disable coloring for that level.
   *
   * @example
   * ```typescript
   * levelColors: {
   *   info: "#00ff00",     // Bright green for info
   *   error: "#ff0000",    // Bright red for errors
   *   warning: "orange",   // ANSI orange for warnings
   *   debug: null,         // No color for debug
   * }
   * ```
   *
   * @default Built-in color scheme (purple trace, blue debug, green info, amber warning, red error, dark red fatal)
   */
  readonly levelColors?: Partial<Record<LogLevel, Color>>;

  /**
   * Visual style applied to log level text.
   *
   * Controls the appearance of the level indicator (e.g., "info", "error").
   * Supports single styles, multiple styles combined, or no styling.
   * Applied in addition to level-specific colors.
   *
   * @example
   * ```typescript
   * levelStyle: "bold"                    // Single style: bold level text
   * levelStyle: "underline"               // Single style: underlined level text
   * levelStyle: ["bold", "underline"]     // Multiple styles: bold + underlined
   * levelStyle: ["dim", "italic"]         // Multiple styles: dimmed + italic
   * levelStyle: null                      // No additional styling
   * ```
   *
   * @default `"underline"`
   */
  readonly levelStyle?: Style;

  /**
   * Icon configuration for each log level.
   *
   * Controls the emoji/symbol displayed before each log entry.
   * Provides visual quick-identification of log severity.
   *
   * - `true`: Use built-in emoji set (🔍 trace, 🐛 debug, ✨ info, ⚠️ warning, ❌ error, 💀 fatal)
   * - `false`: Disable all icons for clean text-only output
   * - Object: Custom icon mapping, falls back to defaults for unspecified levels
   *
   * @example
   * ```typescript
   * icons: true                    // Use default emoji set
   * icons: false                   // No icons
   * icons: {
   *   info: "ℹ️",                  // Custom info icon
   *   error: "🔥",                 // Custom error icon
   *   warning: "⚡",               // Custom warning icon
   * }
   * ```
   *
   * @default `true` (use default emoji icons)
   */
  readonly icons?: boolean | Partial<Record<LogLevel, string>>;

  /**
   * Character(s) used to separate category hierarchy levels.
   *
   * Categories are hierarchical (e.g., ["app", "auth", "jwt"]) and this
   * separator joins them for display (e.g., "app.auth.jwt").
   *
   * @example
   * ```typescript
   * categorySeparator: "·"        // app·auth·jwt
   * categorySeparator: "."        // app.auth.jwt
   * categorySeparator: ":"        // app:auth:jwt
   * categorySeparator: " > "      // app > auth > jwt
   * categorySeparator: "::"       // app::auth::jwt
   * ```
   *
   * @default `"·"` (interpunct)
   */
  readonly categorySeparator?: string;

  /**
   * Default color for category display.
   *
   * Used as fallback when no specific color is found in `categoryColorMap`.
   * Controls the visual appearance of the category hierarchy display.
   *
   * @example
   * ```typescript
   * categoryColor: "#666666"        // Gray categories
   * categoryColor: "blue"           // Blue categories
   * categoryColor: "rgb(100,150,200)" // Light blue categories
   * categoryColor: null             // No coloring
   * ```
   *
   * @default `"rgb(100,116,139)"` (slate gray)
   */
  readonly categoryColor?: Color;

  /**
   * Category-specific color mapping based on prefixes.
   *
   * Maps category prefixes (as arrays) to colors for visual grouping.
   * More specific (longer) prefixes take precedence over shorter ones.
   * If no prefix matches, falls back to the default `categoryColor`.
   *
   * @example
   * ```typescript
   * new Map([
   *   [["app", "auth"], "#ff6b6b"],     // app.auth.* -> red
   *   [["app", "db"], "#4ecdc4"],       // app.db.* -> teal
   *   [["app"], "#45b7d1"],             // app.* (fallback) -> blue
   *   [["lib"], "#96ceb4"],             // lib.* -> green
   * ])
   * ```
   */
  readonly categoryColorMap?: CategoryColorMap;

  /**
   * Visual style applied to category text.
   *
   * Controls the appearance of the category hierarchy display.
   * Supports single styles, multiple styles combined, or no styling.
   * Applied in addition to category colors from `categoryColor` or `categoryColorMap`.
   *
   * @example
   * ```typescript
   * categoryStyle: "dim"                     // Single style: dimmed category text
   * categoryStyle: "italic"                  // Single style: italic category text
   * categoryStyle: ["dim", "italic"]         // Multiple styles: dimmed + italic
   * categoryStyle: ["bold", "underline"]     // Multiple styles: bold + underlined
   * categoryStyle: null                      // No additional styling
   * ```
   *
   * @default `["dim", "italic"]` (dimmed for subtle appearance)
   */
  readonly categoryStyle?: Style;

  /**
   * Maximum display width for category names.
   *
   * Controls layout consistency by limiting category width.
   * Long categories are truncated according to `categoryTruncate` strategy.
   *
   * @default `20`
   */
  readonly categoryWidth?: number;

  /**
   * Strategy for truncating long category names.
   *
   * When categories exceed `categoryWidth`, this controls how truncation works.
   * Smart truncation preserves important context while maintaining layout.
   *
   * - `"middle"`: Keep first and last parts (e.g., "app.server…auth.jwt")
   * - `"end"`: Truncate at the end (e.g., "app.server.middleware…")
   * - `false`: No truncation (ignores `categoryWidth`)
   *
   * @example
   * ```typescript
   * categoryTruncate: "middle"   // app.server…jwt (preserves context)
   * categoryTruncate: "end"      // app.server.midd… (linear truncation)
   * categoryTruncate: false      // app.server.middleware.auth.jwt (full)
   * ```
   *
   * @default `"middle"` (smart context-preserving truncation)
   */
  readonly categoryTruncate?: TruncationStrategy;

  /**
   * Color for log message text content.
   *
   * Controls the visual appearance of the actual log message content.
   * Does not affect structured values, which use syntax highlighting.
   *
   * @example
   * ```typescript
   * messageColor: "#ffffff"        // White message text
   * messageColor: "green"          // Green message text
   * messageColor: "rgb(200,200,200)" // Light gray message text
   * messageColor: null             // No coloring
   * ```
   *
   * @default `"rgb(148,163,184)"` (light slate gray)
   */
  readonly messageColor?: Color;

  /**
   * Visual style applied to log message text.
   *
   * Controls the appearance of the log message content.
   * Supports single styles, multiple styles combined, or no styling.
   * Applied in addition to `messageColor`.
   *
   * @example
   * ```typescript
   * messageStyle: "dim"                      // Single style: dimmed message text
   * messageStyle: "italic"                   // Single style: italic message text
   * messageStyle: ["dim", "italic"]          // Multiple styles: dimmed + italic
   * messageStyle: ["bold", "underline"]      // Multiple styles: bold + underlined
   * messageStyle: null                       // No additional styling
   * ```
   *
   * @default `"dim"` (dimmed for subtle readability)
   */
  readonly messageStyle?: Style;

  /**
   * Global color control for the entire formatter.
   *
   * Master switch to enable/disable all color output.
   * When disabled, produces clean monochrome output suitable for
   * non-color terminals or when colors are not desired.
   *
   * @example
   * ```typescript
   * colors: true     // Full color output (default)
   * colors: false    // Monochrome output only
   * ```
   *
   * @default `true` (colors enabled)
   */
  readonly colors?: boolean;

  /**
   * Column alignment for consistent visual layout.
   *
   * When enabled, ensures all log components (icons, levels, categories)
   * align consistently across multiple log entries, creating a clean
   * tabular appearance.
   *
   * @example
   * ```typescript
   * align: true      // Aligned columns (default)
   * align: false     // Compact, non-aligned output
   * ```
   *
   * @default `true` (alignment enabled)
   */
  readonly align?: boolean;

  /**
   * Configuration for structured value inspection and rendering.
   *
   * Controls how objects, arrays, and other complex values are displayed
   * within log messages. Uses Node.js `util.inspect()` style options.
   *
   * @example
   * ```typescript
   * inspectOptions: {
   *   depth: 3,         // Show 3 levels of nesting
   *   colors: false,    // Disable value syntax highlighting
   *   compact: true,    // Use compact object display
   * }
   * ```
   *
   * @default `{}` (use built-in defaults: depth=unlimited, colors=auto, compact=true)
   */
  readonly inspectOptions?: {
    /**
     * Maximum depth to traverse when inspecting nested objects.
     * @default Infinity (no depth limit)
     */
    readonly depth?: number;

    /**
     * Whether to use syntax highlighting colors for inspected values.
     * @default Inherited from global `colors` setting
     */
    readonly colors?: boolean;

    /**
     * Whether to use compact formatting for objects and arrays.
     * @default `true` (compact formatting)
     */
    readonly compact?: boolean;
  };

  /**
   * Enable word wrapping for long messages.
   *
   * When enabled, long messages will be wrapped at the specified width,
   * with continuation lines aligned to the message column position.
   *
   * - `true`: Auto-detect terminal width when attached to a terminal,
   *   fallback to 80 columns when not in a terminal or detection fails
   * - `number`: Use the specified width in columns
   * - `false`: Disable word wrapping
   *
   * @example
   * ```typescript
   * // Auto-detect terminal width (recommended)
   * wordWrap: true
   *
   * // Custom wrap width
   * wordWrap: 120
   *
   * // Disable word wrapping (default)
   * wordWrap: false
   * ```
   *
   * @default `true` (auto-detect terminal width)
   * @since 1.0.0
   */
  readonly wordWrap?: boolean | number;
}

/**
 * Creates a beautiful console formatter optimized for local development.
 *
 * This formatter provides a Signale-inspired visual design with colorful icons,
 * smart category truncation, dimmed styling, and perfect column alignment.
 * It's specifically designed for development environments that support true colors
 * and Unicode characters.
 *
 * The formatter features:
 * - Emoji icons for each log level (🔍 trace, 🐛 debug, ✨ info, etc.)
 * - True color support with rich color schemes
 * - Intelligent category truncation for long hierarchical categories
 * - Optional timestamp display with multiple formats
 * - Configurable alignment and styling options
 * - Enhanced value rendering with syntax highlighting
 *
 * @param options Configuration options for customizing the formatter behavior.
 * @returns A text formatter function that can be used with LogTape sinks.
 *
 * @example
 * ```typescript
 * import { configure } from "@logtape/logtape";
 * import { getConsoleSink } from "@logtape/logtape/sink";
 * import { getPrettyFormatter } from "@logtape/pretty";
 *
 * await configure({
 *   sinks: {
 *     console: getConsoleSink({
 *       formatter: getPrettyFormatter({
 *         timestamp: "time",
 *         categoryWidth: 25,
 *         icons: {
 *           info: "📘",
 *           error: "🔥"
 *         }
 *       })
 *     })
 *   }
 * });
 * ```
 *
 * @since 1.0.0
 */
export function getPrettyFormatter(
  options: PrettyFormatterOptions = {},
): TextFormatter {
  // Extract options with defaults
  const {
    timestamp = "none",
    timestampColor = "rgb(100,116,139)",
    timestampStyle = "dim",
    level: levelFormat = "full",
    levelColors = {},
    levelStyle = "underline",
    icons = true,
    categorySeparator = "·",
    categoryColor = "rgb(100,116,139)",
    categoryColorMap = new Map(),
    categoryStyle = ["dim", "italic"],
    categoryWidth = 20,
    categoryTruncate = "middle",
    messageColor = "rgb(148,163,184)",
    messageStyle = "dim",
    colors: useColors = true,
    align = true,
    inspectOptions = {},
    wordWrap = true,
  } = options;

  // Resolve icons
  const baseIconMap: Record<LogLevel, string> = icons === false
    ? { trace: "", debug: "", info: "", warning: "", error: "", fatal: "" }
    : icons === true
    ? defaultIcons
    : { ...defaultIcons, ...(icons as Partial<Record<LogLevel, string>>) };

  // Normalize icon spacing for consistent alignment
  const iconMap = normalizeIconSpacing(baseIconMap);

  // Resolve level colors with defaults
  const resolvedLevelColors: Record<LogLevel, Color> = {
    trace: defaultColors.trace,
    debug: defaultColors.debug,
    info: defaultColors.info,
    warning: defaultColors.warning,
    error: defaultColors.error,
    fatal: defaultColors.fatal,
    ...levelColors,
  };

  // Level formatter function with optimized mappings
  const levelMappings: Record<string, Record<LogLevel, string>> = {
    "ABBR": {
      trace: "TRC",
      debug: "DBG",
      info: "INF",
      warning: "WRN",
      error: "ERR",
      fatal: "FTL",
    },
    "L": {
      trace: "T",
      debug: "D",
      info: "I",
      warning: "W",
      error: "E",
      fatal: "F",
    },
    "abbr": {
      trace: "trc",
      debug: "dbg",
      info: "inf",
      warning: "wrn",
      error: "err",
      fatal: "ftl",
    },
    "l": {
      trace: "t",
      debug: "d",
      info: "i",
      warning: "w",
      error: "e",
      fatal: "f",
    },
  };

  const formatLevel = (level: LogLevel): string => {
    if (typeof levelFormat === "function") {
      return levelFormat(level);
    }

    if (levelFormat === "FULL") return level.toUpperCase();
    if (levelFormat === "full") return level;

    return levelMappings[levelFormat]?.[level] ?? level;
  };

  // Timestamp formatters lookup table
  const timestampFormatters: Record<string, (ts: number) => string> = {
    "date-time-timezone": (ts) => {
      const iso = new Date(ts).toISOString();
      return iso.replace("T", " ").replace("Z", " +00:00");
    },
    "date-time-tz": (ts) => {
      const iso = new Date(ts).toISOString();
      return iso.replace("T", " ").replace("Z", " +00");
    },
    "date-time": (ts) => {
      const iso = new Date(ts).toISOString();
      return iso.replace("T", " ").replace("Z", "");
    },
    "time-timezone": (ts) => {
      const iso = new Date(ts).toISOString();
      return iso.replace(/.*T/, "").replace("Z", " +00:00");
    },
    "time-tz": (ts) => {
      const iso = new Date(ts).toISOString();
      return iso.replace(/.*T/, "").replace("Z", " +00");
    },
    "time": (ts) => {
      const iso = new Date(ts).toISOString();
      return iso.replace(/.*T/, "").replace("Z", "");
    },
    "date": (ts) => new Date(ts).toISOString().replace(/T.*/, ""),
    "rfc3339": (ts) => new Date(ts).toISOString(),
  };

  // Resolve timestamp formatter
  let timestampFn: ((ts: number) => string | null) | null = null;
  if (timestamp === "none" || timestamp === "disabled") {
    timestampFn = null;
  } else if (typeof timestamp === "function") {
    timestampFn = timestamp;
  } else {
    timestampFn = timestampFormatters[timestamp as string] ?? null;
  }

  // Configure word wrap settings
  const wordWrapEnabled = wordWrap !== false;
  let wordWrapWidth: number;

  if (typeof wordWrap === "number") {
    wordWrapWidth = wordWrap;
  } else if (wordWrap === true) {
    // Auto-detect terminal width
    wordWrapWidth = getOptimalWordWrapWidth(80);
  } else {
    wordWrapWidth = 80; // Default fallback
  }

  // Prepare category color patterns for matching
  const categoryPatterns = prepareCategoryPatterns(categoryColorMap);

  // Calculate level width based on format
  const allLevels: LogLevel[] = [
    "trace",
    "debug",
    "info",
    "warning",
    "error",
    "fatal",
  ];
  const levelWidth = Math.max(...allLevels.map((l) => formatLevel(l).length));

  return (record: LogRecord): string => {
    // Calculate the prefix parts first to determine message column position
    const icon = iconMap[record.level] || "";
    const level = formatLevel(record.level);
    const categoryStr = truncateCategory(
      record.category,
      categoryWidth,
      categorySeparator,
      categoryTruncate,
    );

    // Format message with values - handle color reset/reapply for interpolated values
    let message = "";
    const messageColorCode = useColors ? colorToAnsi(messageColor) : "";
    const messageStyleCode = useColors ? styleToAnsi(messageStyle) : "";
    const messagePrefix = useColors
      ? `${messageStyleCode}${messageColorCode}`
      : "";

    for (let i = 0; i < record.message.length; i++) {
      if (i % 2 === 0) {
        message += record.message[i];
      } else {
        const value = record.message[i];
        const inspected = inspect(value, {
          colors: useColors,
          ...inspectOptions,
        });

        // Handle multiline interpolated values properly
        if (inspected.includes("\n")) {
          const lines = inspected.split("\n");

          const formattedLines = lines.map((line, index) => {
            if (index === 0) {
              // First line: reset formatting, add the line, then reapply
              if (useColors && (messageColorCode || messageStyleCode)) {
                return `${RESET}${line}${messagePrefix}`;
              } else {
                return line;
              }
            } else {
              // Continuation lines: just apply formatting, let wrapText handle indentation
              if (useColors && (messageColorCode || messageStyleCode)) {
                return `${line}${messagePrefix}`;
              } else {
                return line;
              }
            }
          });
          message += formattedLines.join("\n");
        } else {
          // Single line - handle normally
          if (useColors && (messageColorCode || messageStyleCode)) {
            message += `${RESET}${inspected}${messagePrefix}`;
          } else {
            message += inspected;
          }
        }
      }
    }

    // Parts are already calculated above

    // Determine category color (with prefix matching)
    const finalCategoryColor = useColors
      ? (matchCategoryColor(record.category, categoryPatterns) || categoryColor)
      : null;

    // Apply colors and styling
    const formattedIcon = icon;
    let formattedLevel = level;
    let formattedCategory = categoryStr;
    let formattedMessage = message;
    let formattedTimestamp = "";

    if (useColors) {
      // Apply level color and style
      const levelColorCode = colorToAnsi(resolvedLevelColors[record.level]);
      const levelStyleCode = styleToAnsi(levelStyle);
      formattedLevel = `${levelStyleCode}${levelColorCode}${level}${RESET}`;

      // Apply category color and style (with prefix matching)
      const categoryColorCode = colorToAnsi(finalCategoryColor);
      const categoryStyleCode = styleToAnsi(categoryStyle);
      formattedCategory =
        `${categoryStyleCode}${categoryColorCode}${categoryStr}${RESET}`;

      // Apply message color and style (already handled in message building above)
      formattedMessage = `${messagePrefix}${message}${RESET}`;
    }

    // Format timestamp if needed
    if (timestampFn) {
      const ts = timestampFn(record.timestamp);
      if (ts !== null) {
        if (useColors) {
          const timestampColorCode = colorToAnsi(timestampColor);
          const timestampStyleCode = styleToAnsi(timestampStyle);
          formattedTimestamp =
            `${timestampStyleCode}${timestampColorCode}${ts}${RESET}  `;
        } else {
          formattedTimestamp = `${ts}  `;
        }
      }
    }

    // Build the final output with alignment
    if (align) {
      // Calculate padding accounting for ANSI escape sequences
      const levelColorLength = useColors
        ? (colorToAnsi(resolvedLevelColors[record.level]).length +
          styleToAnsi(levelStyle).length + RESET.length)
        : 0;
      const categoryColorLength = useColors
        ? (colorToAnsi(finalCategoryColor).length +
          styleToAnsi(categoryStyle).length + RESET.length)
        : 0;

      const paddedLevel = formattedLevel.padEnd(levelWidth + levelColorLength);
      const paddedCategory = formattedCategory.padEnd(
        categoryWidth + categoryColorLength,
      );

      let result =
        `${formattedTimestamp}${formattedIcon} ${paddedLevel} ${paddedCategory} ${formattedMessage}`;

      // Apply word wrapping if enabled, or if there are multiline interpolated values
      if (wordWrapEnabled || message.includes("\n")) {
        result = wrapText(
          result,
          wordWrapEnabled ? wordWrapWidth : Infinity,
          message,
        );
      }

      return result + "\n";
    } else {
      let result =
        `${formattedTimestamp}${formattedIcon} ${formattedLevel} ${formattedCategory} ${formattedMessage}`;

      // Apply word wrapping if enabled, or if there are multiline interpolated values
      if (wordWrapEnabled || message.includes("\n")) {
        result = wrapText(
          result,
          wordWrapEnabled ? wordWrapWidth : Infinity,
          message,
        );
      }

      return result + "\n";
    }
  };
}

/**
 * A pre-configured beautiful console formatter for local development.
 *
 * This is a ready-to-use instance of the pretty formatter with sensible defaults
 * for most development scenarios. It provides immediate visual enhancement to
 * your logs without requiring any configuration.
 *
 * Features enabled by default:
 * - Emoji icons for all log levels
 * - True color support with rich color schemes
 * - Dimmed text styling for better readability
 * - Smart category truncation (20 characters max)
 * - Perfect column alignment
 * - No timestamp display (cleaner for development)
 *
 * For custom configuration, use {@link getPrettyFormatter} instead.
 *
 * @example
 * ```typescript
 * import { configure } from "@logtape/logtape";
 * import { getConsoleSink } from "@logtape/logtape/sink";
 * import { prettyFormatter } from "@logtape/pretty";
 *
 * await configure({
 *   sinks: {
 *     console: getConsoleSink({
 *       formatter: prettyFormatter
 *     })
 *   }
 * });
 * ```
 *
 * @since 1.0.0
 */
export const prettyFormatter: TextFormatter = getPrettyFormatter();
