#!/bin/bash

# omarchy:summary=Generate themed config files from Omarchy templates
# omarchy:hidden=true

TEMPLATES_DIR="$OMARCHY_PATH/default/themed"
USER_TEMPLATES_DIR="$HOME/.config/omarchy/themed"
NEXT_THEME_DIR="$HOME/.config/omarchy/current/next-theme"
COLORS_FILE="$NEXT_THEME_DIR/colors.toml"

declare -A THEME_COLORS

# Convert hex color to decimal RGB (e.g., "#1e1e2e" -> "30,30,46")
hex_to_rgb() {
  local hex="${1#\#}"
  printf "%d,%d,%d" "0x${hex:0:2}" "0x${hex:2:2}" "0x${hex:4:2}"
}

# Mix two hex colors. Amount may be a fraction (0.30) or percentage (30%).
mix_color() {
  local start="${1#\#}"
  local end="${2#\#}"
  local amount="$3"

  awk -v start="$start" -v end="$end" -v amount="$amount" '
    function hex_value(char) {
      return index("0123456789abcdef", tolower(char)) - 1
    }

    function hex_pair_to_int(hex, idx) {
      return hex_value(substr(hex, idx, 1)) * 16 + hex_value(substr(hex, idx + 1, 1))
    }

    BEGIN {
      if (amount ~ /%$/) {
        sub(/%$/, "", amount)
        amount = amount / 100
      } else {
        amount += 0
        if (amount > 1) amount = amount / 100
      }

      if (amount < 0) amount = 0
      if (amount > 1) amount = 1

      start_r = hex_pair_to_int(start, 1)
      start_g = hex_pair_to_int(start, 3)
      start_b = hex_pair_to_int(start, 5)
      end_r = hex_pair_to_int(end, 1)
      end_g = hex_pair_to_int(end, 3)
      end_b = hex_pair_to_int(end, 5)

      red = int(start_r * (1 - amount) + end_r * amount + 0.5)
      green = int(start_g * (1 - amount) + end_g * amount + 0.5)
      blue = int(start_b * (1 - amount) + end_b * amount + 0.5)

      printf "#%02x%02x%02x\n", red, green, blue
    }
  '
}

trim() {
  local value="$1"

  value="${value#"${value%%[![:space:]]*}"}"
  value="${value%"${value##*[![:space:]]}"}"
  printf "%s" "$value"
}

resolve_theme_ref() {
  local ref="$1"
  local fallback="${2:-}"

  if [[ -n ${THEME_COLORS[$ref]+_} ]]; then
    printf "%s" "${THEME_COLORS[$ref]}"
  elif [[ -n $fallback && -n ${THEME_COLORS[$fallback]+_} ]]; then
    printf "%s" "${THEME_COLORS[$fallback]}"
  elif [[ -n $fallback ]]; then
    printf "%s" "$fallback"
  else
    printf "%s" "$ref"
  fi
}

resolve_gradient_color() {
  local color

  color=$(trim "$1")
  if [[ -n ${THEME_COLORS[$color]+_} ]]; then
    color="${THEME_COLORS[$color]}"
  fi

  printf "%s" "$color"
}

parse_gradient() {
  local spec="$1"
  local part color
  local -a parts

  GRADIENT_COLORS=()
  GRADIENT_ANGLE=""

  read -ra parts <<<"$spec"
  for part in "${parts[@]}"; do
    [[ -n $part ]] || continue

    if [[ $part =~ ^-?[0-9]+([.][0-9]+)?deg$ ]]; then
      GRADIENT_ANGLE="${part%deg}"
    else
      color=$(resolve_gradient_color "$part")
      GRADIENT_COLORS+=("$color")
    fi
  done
}

color_to_shell_hex() {
  local color r g b

  color=$(resolve_gradient_color "$1")

  if [[ $color =~ ^#[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?$ ]]; then
    printf "#%s" "${color:1:6}"
  elif [[ $color =~ ^[Rr][Gg][Bb][Aa]?\(([0-9A-Fa-f]{6})([0-9A-Fa-f]{2})?\)$ ]]; then
    printf "#%s" "${BASH_REMATCH[1]}"
  elif [[ $color =~ ^[Rr][Gg][Bb][Aa]?\(([0-9]+),([0-9]+),([0-9]+)(,[0-9.]+)?\)$ ]]; then
    r=${BASH_REMATCH[1]}
    g=${BASH_REMATCH[2]}
    b=${BASH_REMATCH[3]}
    (( r > 255 )) && r=255
    (( g > 255 )) && g=255
    (( b > 255 )) && b=255
    printf "#%02x%02x%02x" "$r" "$g" "$b"
  elif [[ $color =~ ^0x[0-9A-Fa-f]{8}$ ]]; then
    printf "#%s" "${color:4:6}"
  else
    printf "%s" "$color"
  fi
}

hypr_gradient_value() {
  local spec color index

  spec=$(resolve_theme_ref "$1" "${2:-}")
  parse_gradient "$spec"

  if (( ${#GRADIENT_COLORS[@]} == 0 )); then
    printf '"%s"' "$spec"
  elif (( ${#GRADIENT_COLORS[@]} == 1 )); then
    printf '"%s"' "${GRADIENT_COLORS[0]}"
  else
    printf '{ colors = {'
    for index in "${!GRADIENT_COLORS[@]}"; do
      (( index > 0 )) && printf ','
      printf ' "%s"' "${GRADIENT_COLORS[$index]}"
    done
    printf ' }'
    [[ -n $GRADIENT_ANGLE ]] && printf ', angle = %s' "$GRADIENT_ANGLE"
    printf ' }'
  fi
}

gradient_start_value() {
  local spec

  spec=$(resolve_theme_ref "$1" "${2:-}")
  parse_gradient "$spec"

  if (( ${#GRADIENT_COLORS[@]} == 0 )); then
    color_to_shell_hex "$spec"
  else
    color_to_shell_hex "${GRADIENT_COLORS[0]}"
  fi
}

shell_gradient_value() {
  local spec index

  spec=$(resolve_theme_ref "$1" "${2:-}")
  parse_gradient "$spec"

  if (( ${#GRADIENT_COLORS[@]} == 0 )); then
    printf "%s" "$spec"
    return
  fi

  for index in "${!GRADIENT_COLORS[@]}"; do
    (( index > 0 )) && printf " "
    printf "%s" "${GRADIENT_COLORS[$index]}"
  done

  [[ -n $GRADIENT_ANGLE ]] && printf " %sdeg" "$GRADIENT_ANGLE"
}

add_template_value() {
  local key="$1"
  local value="$2"
  local rgb

  printf 's|{{ %s }}|%s|g\n' "$key" "$value" >>"$sed_script"
  printf 's|{{ %s_strip }}|%s|g\n' "$key" "${value#\#}" >>"$sed_script"

  if [[ $value =~ ^#[0-9A-Fa-f]{6}$ ]]; then
    rgb=$(hex_to_rgb "$value")
    printf 's|{{ %s_rgb }}|%s|g\n' "$key" "$rgb" >>"$sed_script"
  fi
}

add_mix_value() {
  local token="$1"
  local content fn start_key end_key amount start end value

  content="${token#\{\{}"
  content="${content%\}\}}"
  read -r fn start_key end_key amount <<<"$content"

  start="${THEME_COLORS[$start_key]:-}"
  end="${THEME_COLORS[$end_key]:-}"

  [[ $start =~ ^#[0-9A-Fa-f]{6}$ && $end =~ ^#[0-9A-Fa-f]{6}$ ]] || return

  value=$(mix_color "$start" "$end" "$amount")

  case "$fn" in
    mix)
      ;;
    mix_strip)
      value="${value#\#}"
      ;;
    mix_rgb)
      value=$(hex_to_rgb "$value")
      ;;
    *)
      return
      ;;
  esac

  printf 's|%s|%s|g\n' "$token" "$value" >>"$sed_script"
}

add_mix_values() {
  local tpl token
  local -A seen=()

  for tpl in "${template_files[@]}"; do
    while IFS= read -r token; do
      [[ -n ${seen[$token]:-} ]] && continue
      seen[$token]=1
      add_mix_value "$token"
    done < <(grep -hEo '\{\{[[:space:]]*mix(_strip|_rgb)?[[:space:]]+[A-Za-z0-9_]+[[:space:]]+[A-Za-z0-9_]+[[:space:]]+[0-9]+([.][0-9]+)?%?[[:space:]]*\}\}' "$tpl" 2>/dev/null || true)
  done
}

add_gradient_function_value() {
  local token="$1"
  local content fn key fallback value

  content="${token#\{\{}"
  content="${content%\}\}}"
  read -r fn key fallback <<<"$content"

  case "$fn" in
    hypr_gradient)
      value=$(hypr_gradient_value "$key" "$fallback")
      ;;
    gradient_start)
      value=$(gradient_start_value "$key" "$fallback")
      ;;
    shell_gradient)
      value=$(shell_gradient_value "$key" "$fallback")
      ;;
    *)
      return
      ;;
  esac

  printf 's|%s|%s|g\n' "$token" "$value" >>"$sed_script"
}

add_gradient_function_values() {
  local tpl token
  local -A seen=()

  for tpl in "${template_files[@]}"; do
    while IFS= read -r token; do
      [[ -n ${seen[$token]:-} ]] && continue
      seen[$token]=1
      add_gradient_function_value "$token"
    done < <(grep -hEo '\{\{[[:space:]]*(hypr_gradient|gradient_start|shell_gradient)[[:space:]]+[^}]+[[:space:]]*\}\}' "$tpl" 2>/dev/null || true)
  done
}

strip_shell_section_header() {
  local section="$1"
  local file="$2"

  awk -v section="$section" '
    BEGIN { skipping_header = 0 }
    /^[[:space:]]*\[[^]]+\][[:space:]]*($|#)/ {
      if ($0 ~ "^[[:space:]]*\\[" section "\\][[:space:]]*($|#)") {
        skipping_header = 1
        next
      }
    }
    { print }
  ' "$file"
}

apply_shell_section_override() {
  local override="$1"
  local section tmp body

  [[ -f $NEXT_THEME_DIR/shell.toml ]] || return
  [[ -f $override ]] || return

  section=$(basename "$override")
  section="${section#shell.}"
  section="${section%.toml}"
  [[ $section =~ ^[A-Za-z0-9_-]+$ ]] || return

  tmp=$(mktemp)
  body=$(mktemp)

  strip_shell_section_header "$section" "$override" >"$body"

  awk -v section="$section" -v body="$body" '
    function emit_override() {
      if (emitted) return
      print "[" section "]"
      while ((getline line < body) > 0) print line
      close(body)
      emitted = 1
    }

    /^[[:space:]]*\[[^]]+\][[:space:]]*($|#)/ {
      if (in_section) {
        emit_override()
        in_section = 0
      }
      if ($0 ~ "^[[:space:]]*\\[" section "\\][[:space:]]*($|#)") {
        in_section = 1
        next
      }
    }

    !in_section { print }

    END {
      if (in_section || !emitted) {
        if (NR > 0) print ""
        emit_override()
      }
    }
  ' "$NEXT_THEME_DIR/shell.toml" >"$tmp"

  mv "$tmp" "$NEXT_THEME_DIR/shell.toml"
  rm "$body"
}

apply_shell_section_overrides() {
  local override

  shopt -s nullglob
  for override in "$NEXT_THEME_DIR"/shell.*.toml; do
    [[ $(basename "$override") != "shell.toml" ]] || continue
    apply_shell_section_override "$override"
  done
}

alias_theme_color() {
  local key="$1"
  local fallback="$2"

  [[ ${THEME_COLORS[$key]} ]] || THEME_COLORS[$key]="${THEME_COLORS[$fallback]}"
}

resolve_theme_mode() {
  local bg_hex lum

  [[ ${THEME_COLORS[mode]} ]] || THEME_COLORS[mode]="${THEME_COLORS[theme_type]}"
  [[ ${THEME_COLORS[mode]} ]] && return

  if [[ -f $NEXT_THEME_DIR/light.mode ]]; then
    THEME_COLORS[mode]="light"
  elif [[ ${THEME_COLORS[background]} =~ ^#[0-9A-Fa-f]{6}$ ]]; then
    bg_hex="${THEME_COLORS[background]#\#}"
    lum=$(( $(printf "%d" "0x${bg_hex:0:2}") + $(printf "%d" "0x${bg_hex:2:2}") + $(printf "%d" "0x${bg_hex:4:2}") ))
    (( lum > 382 )) && THEME_COLORS[mode]="light" || THEME_COLORS[mode]="dark"
  else
    THEME_COLORS[mode]="dark"
  fi
}

# Only generate dynamic templates for themes with a colors.toml definition
if [[ -f $COLORS_FILE ]]; then
  sed_script=$(mktemp)

  shopt -s nullglob
  template_files=("$USER_TEMPLATES_DIR"/*.tpl "$TEMPLATES_DIR"/*.tpl)

  while IFS='=' read -r key value; do
    key="${key//[\"\' ]/}"                # strip quotes and spaces from key
    [[ $key && $key != \#* ]] || continue # skip empty lines and comments
    value="${value#*[\"\']}"
    value="${value%%[\"\']*}" # extract value between quotes (ignores inline comments)

    THEME_COLORS[$key]="$value"
  done <"$COLORS_FILE"

  # Legacy compatibility: map ANSI color0..color15 and raw bg/fg to semantic names
  declare -A legacy_alias=(
    [bg]=background
    [fg]=foreground
    [red]=color1
    [green]=color2
    [yellow]=color3
    [blue]=color4
    [magenta]=color5
    [cyan]=color6
    [bright_red]=color9
    [bright_green]=color10
    [bright_yellow]=color11
    [bright_blue]=color12
    [bright_magenta]=color13
    [bright_cyan]=color14
  )
  for key in "${!legacy_alias[@]}"; do
    alias_theme_color "$key" "${legacy_alias[$key]}"
  done
  alias_theme_color magenta purple
  alias_theme_color bright_magenta bright_purple

  [[ ${THEME_COLORS[light_fg]} ]]   || THEME_COLORS[light_fg]="${THEME_COLORS[color7]:-${THEME_COLORS[foreground]}}"
  [[ ${THEME_COLORS[bright_fg]} ]]  || THEME_COLORS[bright_fg]="${THEME_COLORS[color15]:-${THEME_COLORS[foreground]}}"
  [[ ${THEME_COLORS[lighter_bg]} ]] || THEME_COLORS[lighter_bg]="${THEME_COLORS[color0]:-${THEME_COLORS[background]}}"
  [[ ${THEME_COLORS[dark_fg]} ]]    || THEME_COLORS[dark_fg]="${THEME_COLORS[color8]:-${THEME_COLORS[foreground]}}"
  [[ ${THEME_COLORS[muted]} ]]      || THEME_COLORS[muted]="${THEME_COLORS[color8]:-${THEME_COLORS[dark_fg]}}"
  [[ ${THEME_COLORS[selection]} ]]  || THEME_COLORS[selection]="${THEME_COLORS[selection_background]:-${THEME_COLORS[color8]:-${THEME_COLORS[color0]}}}"
  [[ ${THEME_COLORS[orange]} ]]     || THEME_COLORS[orange]="${THEME_COLORS[yellow]}"
  [[ ${THEME_COLORS[brown]} ]]      || THEME_COLORS[brown]=$(mix_color "${THEME_COLORS[orange]}" "#000000" 50%)

  # Auto-derive shades from base accents when not defined and not aliased from colorN
  [[ ${THEME_COLORS[dark_bg]} ]]       || THEME_COLORS[dark_bg]=$(mix_color "${THEME_COLORS[background]}" "#000000" 25%)
  [[ ${THEME_COLORS[darker_bg]} ]]     || THEME_COLORS[darker_bg]=$(mix_color "${THEME_COLORS[background]}" "#000000" 50%)
  [[ ${THEME_COLORS[bright_red]} ]]    || THEME_COLORS[bright_red]=$(mix_color "${THEME_COLORS[red]}" "#ffffff" 20%)
  [[ ${THEME_COLORS[bright_yellow]} ]] || THEME_COLORS[bright_yellow]=$(mix_color "${THEME_COLORS[yellow]}" "#ffffff" 20%)
  [[ ${THEME_COLORS[bright_green]} ]]  || THEME_COLORS[bright_green]=$(mix_color "${THEME_COLORS[green]}" "#ffffff" 20%)
  [[ ${THEME_COLORS[bright_cyan]} ]]   || THEME_COLORS[bright_cyan]=$(mix_color "${THEME_COLORS[cyan]}" "#ffffff" 20%)
  [[ ${THEME_COLORS[bright_blue]} ]]   || THEME_COLORS[bright_blue]=$(mix_color "${THEME_COLORS[blue]}" "#ffffff" 20%)
  [[ ${THEME_COLORS[bright_magenta]} ]] || THEME_COLORS[bright_magenta]=$(mix_color "${THEME_COLORS[magenta]}" "#ffffff" 20%)
  alias_theme_color purple magenta
  alias_theme_color bright_purple bright_magenta

  # Keep semantic themes compatible with user templates that still reference
  # the legacy ANSI placeholders directly.
  declare -A ansi_alias=(
    [color0]=bg
    [color1]=red
    [color2]=green
    [color3]=yellow
    [color4]=blue
    [color5]=magenta
    [color6]=cyan
    [color7]=fg
    [color8]=muted
    [color9]=bright_red
    [color10]=bright_green
    [color11]=bright_yellow
    [color12]=bright_blue
    [color13]=bright_magenta
    [color14]=bright_cyan
    [color15]=bright_fg
  )
  for key in "${!ansi_alias[@]}"; do
    alias_theme_color "$key" "${ansi_alias[$key]}"
  done

  # Resolve theme mode (dark|light) with this precedence:
  #   1. `mode` key in colors.toml
  #   2. legacy `light.mode` file shipped beside the theme
  #   3. background luminance auto-detect
  # `theme_type` is kept as an alias so existing templates ({{ theme_type }}) keep working.
  resolve_theme_mode
  THEME_COLORS[theme_type]="${THEME_COLORS[mode]}"

  for key in "${!THEME_COLORS[@]}"; do
    add_template_value "$key" "${THEME_COLORS[$key]}"
  done

  add_mix_values
  add_gradient_function_values

  # Process user templates first, then built-in templates (user overrides built-in)
  for tpl in "${template_files[@]}"; do
    filename=$(basename "$tpl" .tpl)
    output_path="$NEXT_THEME_DIR/$filename"

    # Don't overwrite configs already exists in the output directory (copied from theme specific folder)
    if [[ ! -f $output_path ]]; then
      sed -f "$sed_script" "$tpl" >"$output_path"
    fi
  done

  rm "$sed_script"
fi

apply_shell_section_overrides
