#!/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/.local/state/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
}

# 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)

  # Parsing and alias/fallback resolution (legacy colorN names, derived
  # shades, mode/theme_type) is shared with every other colors.toml consumer.
  while IFS=$'\t' read -r key value; do
    THEME_COLORS[$key]="$value"
  done < <(omarchy-theme-color --file "$COLORS_FILE" --all)

  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
