use anyhow::{anyhow, Result};
use std::collections::BTreeMap;

/// A preset theme. Fields mirror StatusSpec; None means "don't impose a default".
pub struct Theme {
    pub style: BTreeMap<String, String>,
    pub left: Option<String>,
    pub right: Option<String>,
    pub window_format: Option<String>,
    pub current_window_format: Option<String>,
    pub status_interval: Option<u32>,
    /// Helper scripts the theme expects to be materialized into ~/.sessionx/segments/.
    pub builtin_segments: Vec<(&'static str, &'static str)>,
}

/// Resolved format strings with id-style placeholders substituted.
pub struct Resolved {
    pub left: Option<String>,
    pub right: Option<String>,
    pub window_format: Option<String>,
    pub current_window_format: Option<String>,
}

impl Theme {
    pub fn resolve(&self, win_style: &str, pane_style: &str) -> Resolved {
        let sub = |s: &Option<String>| -> Option<String> {
            s.as_ref().map(|s| {
                s.replace("__WIN_STYLE__", win_style)
                    .replace("__PANE_STYLE__", pane_style)
            })
        };
        Resolved {
            left: sub(&self.left),
            right: sub(&self.right),
            window_format: sub(&self.window_format),
            current_window_format: sub(&self.current_window_format),
        }
    }
}

pub const ID_STYLES: &[&str] = &[
    "fsquare", "hsquare", "dsquare", "super", "sub", "roman", "digital", "none", "hide",
];

pub fn validate_id_style(name: &str) -> Result<()> {
    if ID_STYLES.contains(&name) {
        Ok(())
    } else {
        Err(anyhow!(
            "unknown id style '{name}'. Available: {}",
            ID_STYLES.join(", ")
        ))
    }
}

pub fn list() -> &'static [&'static str] {
    &[
        "tokyo-night",
        "catppuccin",
        "dracula",
        "gruvbox",
        "nord",
        "rose-pine",
        "minimal",
    ]
}

pub fn load(name: &str) -> Result<Theme> {
    match name {
        "tokyo-night" => Ok(tokyo_night()),
        "catppuccin" => Ok(catppuccin()),
        "dracula" => Ok(dracula()),
        "gruvbox" => Ok(gruvbox()),
        "nord" => Ok(nord()),
        "rose-pine" => Ok(rose_pine()),
        "minimal" => Ok(minimal()),
        other => Err(anyhow!(
            "unknown theme '{other}'. Available: {}",
            list().join(", ")
        )),
    }
}

fn s(map: &mut BTreeMap<String, String>, k: &str, v: &str) {
    map.insert(k.to_string(), v.to_string());
}

// Helper scripts ported from janoamaral/tokyo-night-tmux (MIT, © Lógico) — trimmed.

const FMT_NUM_SH: &str = r#"#!/usr/bin/env bash
# auto-generated by sessionx — do not edit
# Translates a number to NerdFont glyphs in the chosen style.
format_hide=""
format_none="0123456789"
format_digital="🯰🯱🯲🯳🯴🯵🯶🯷🯸🯹"
format_fsquare="󰎡󰎤󰎧󰎪󰎭󰎱󰎳󰎶󰎹󰎼"
format_hsquare="󰎣󰎦󰎩󰎬󰎮󰎰󰎵󰎸󰎻󰎾"
format_dsquare="󰎢󰎥󰎨󰎫󰎲󰎯󰎴󰎷󰎺󰎽"
format_roman=" 󱂈󱂉󱂊󱂋󱂌󱂍󱂎󱂏󱂐"
format_super="⁰¹²³⁴⁵⁶⁷⁸⁹"
format_sub="₀₁₂₃₄₅₆₇₈₉"

ID="${1:-0}"
FORMAT="${2:-none}"

[ "$FORMAT" = "hide" ] && exit 0

format="$(eval echo \"\$format_${FORMAT}\")"
if [ -z "$format" ]; then
  echo -n "$ID"
  exit 0
fi

if [ "$FORMAT" = "roman" ] && [ ${#ID} -gt 1 ]; then
  echo -n "$ID "
else
  for ((i = 0; i < ${#ID}; i++)); do
    DIGIT="${ID:i:1}"
    case "$DIGIT" in
      [0-9]) echo -n "${format:DIGIT:1} " ;;
      *) echo -n "$DIGIT" ;;
    esac
  done
fi
"#;

const OS_ICON_SH: &str = r#"#!/usr/bin/env bash
# auto-generated by sessionx — do not edit
# Prints a NerdFont OS icon for the current host.
case "$OSTYPE" in
  darwin*) echo -n "" ;;
  linux-gnu*)
    if [ -r /etc/os-release ]; then
      . /etc/os-release
      case "$ID" in
        ubuntu)   echo -n "" ;;
        fedora)   echo -n "" ;;
        arch)     echo -n "" ;;
        debian)   echo -n "" ;;
        centos)   echo -n "" ;;
        manjaro)  echo -n "" ;;
        alpine)   echo -n "" ;;
        kali)     echo -n "" ;;
        opensuse*) echo -n "" ;;
        *)        echo -n "" ;;
      esac
    else
      echo -n ""
    fi ;;
  freebsd*|openbsd*) echo -n "" ;;
  cygwin|msys|win32) echo -n "" ;;
  *) echo -n "" ;;
esac
"#;

const GIT_STATUS_SH: &str = r#"#!/usr/bin/env bash
# auto-generated by sessionx — do not edit
# Prints "<branch-glyph> <branch> <dirty-or-clean-dot>" for the given path; empty if not a repo.
set +e
cd "${1:-.}" 2>/dev/null || exit 0
BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
[ -z "$BRANCH" ] && exit 0
if [ "${#BRANCH}" -gt 25 ]; then
  BRANCH="${BRANCH:0:25}…"
fi
if [ -n "$(git status --porcelain 2>/dev/null)" ]; then
  DOT="●"
else
  DOT="○"
fi
echo -n " $BRANCH $DOT"
"#;

fn shared_segments() -> Vec<(&'static str, &'static str)> {
    vec![
        ("sx-fmt-num", FMT_NUM_SH),
        ("sx-os-icon", OS_ICON_SH),
        ("sx-git-status", GIT_STATUS_SH),
    ]
}

struct Palette {
    bg: &'static str,
    fg: &'static str,
    /// Recedes into the bar; used for inactive-window glyphs and right-side block bg.
    dim: &'static str,
    /// Mid-tone for inactive window names — readable but clearly not focused.
    muted: &'static str,
    accent: &'static str,
    accent2: &'static str,
    badge: &'static str,
    /// Background for the session-name block. Must be distinct from `dim`
    /// (the active-window pill bg) so the two blocks don't visually merge.
    session_bg: &'static str,
    icon: &'static str,
    git: bool,
}

fn build_left(p: &Palette) -> String {
    format!(
        "#[fg={bg},bg={accent},bold] {icon} #(~/.sessionx/segments/sx-os-icon.sh)\
         #[fg={fg},bg={badge},nobold] #h \
         #[fg={fg},bg={session_bg},bold] #{{?client_prefix,󰠠 ,#[dim]󰤂 }}#[bold,nodim]#S \
         #[fg={session_bg},bg={bg},nobold]{pill_right} ",
        bg = p.bg,
        accent = p.accent,
        icon = p.icon,
        fg = p.fg,
        badge = p.badge,
        session_bg = p.session_bg,
        pill_right = PILL_RIGHT,
    )
}

// Active vs inactive contrast strategy:
//   inactive — flat against bar `bg`, name in `muted`, glyphs in `dim`
//   active   — raised pill on `dim` bg, accent icon + bright bold name,
//              bookended by triangle separators that paint the pill edges
//              cleanly against the bar background.
const PILL_LEFT: &str = ""; //
const PILL_RIGHT: &str = ""; //

fn build_window_format(p: &Palette) -> String {
    format!(
        " #[fg={dim},bg={bg}]#{{?#{{==:#{{pane_current_command}},ssh}},󰣀 , }}\
         #(~/.sessionx/segments/sx-fmt-num.sh #I __WIN_STYLE__)\
         #[fg={muted}]#W\
         #[fg={dim}] #(~/.sessionx/segments/sx-fmt-num.sh #{{window_panes}} __PANE_STYLE__)\
         #[fg={dim}]#{{?window_last_flag,󰁯 , }} ",
        dim = p.dim,
        bg = p.bg,
        muted = p.muted
    )
}

fn build_current_window_format(p: &Palette) -> String {
    format!(
        "#[fg={dim},bg={bg}]{pill_left}\
         #[fg={accent},bg={dim},bold]#{{?#{{==:#{{pane_current_command}},ssh}},󰣀 , }}\
         #(~/.sessionx/segments/sx-fmt-num.sh #I __WIN_STYLE__)\
         #[fg={fg},bg={dim},bold]#W\
         #[fg={accent2},bg={dim},nobold] #(~/.sessionx/segments/sx-fmt-num.sh #{{window_panes}} __PANE_STYLE__)\
         #[fg={accent2},bg={dim}]#{{?window_last_flag,󰁯 , }}\
         #[fg={dim},bg={bg}]{pill_right}",
        dim = p.dim,
        bg = p.bg,
        accent = p.accent,
        accent2 = p.accent2,
        fg = p.fg,
        pill_left = PILL_LEFT,
        pill_right = PILL_RIGHT,
    )
}

fn build_right(p: &Palette) -> String {
    let git_seg = if p.git {
        format!(
            "#[fg={fg},bg={dim}]#(~/.sessionx/segments/sx-git-status.sh #{{pane_current_path}}) ",
            fg = p.fg,
            dim = p.dim
        )
    } else {
        String::new()
    };
    format!(
        "#[fg={dim},bg={bg}]{git}\
         #[fg={fg},bg={dim}] %m-%d-%Y \
         #[fg={accent2}]< \
         #[fg={fg}]%I:%M %p ",
        dim = p.dim,
        bg = p.bg,
        fg = p.fg,
        accent2 = p.accent2,
        git = git_seg
    )
}

fn theme_from(p: Palette, mut style: BTreeMap<String, String>) -> Theme {
    let left = build_left(&p);
    let win = build_window_format(&p);
    let cur = build_current_window_format(&p);
    let right = build_right(&p);
    s(
        &mut style,
        "status-style",
        &format!("bg={},fg={}", p.bg, p.fg),
    );
    Theme {
        style,
        left: Some(left),
        right: Some(right),
        window_format: Some(win),
        current_window_format: Some(cur),
        status_interval: Some(5),
        builtin_segments: shared_segments(),
    }
}

fn tokyo_night() -> Theme {
    let mut style = BTreeMap::new();
    s(&mut style, "window-status-style", "bg=#1a1b26,fg=#7aa2f7");
    s(
        &mut style,
        "window-status-current-style",
        "bg=#1a1b26,fg=#bb9af7,bold",
    );
    s(&mut style, "pane-active-border-style", "fg=#7aa2f7");
    s(&mut style, "pane-border-style", "fg=#414868");
    s(&mut style, "message-style", "bg=#bb9af7,fg=#1a1b26,bold");
    theme_from(
        Palette {
            bg: "#1a1b26",
            fg: "#c0caf5",
            dim: "#2a2c3d",
            muted: "#7aa2f7",
            accent: "#bb9af7",
            accent2: "#7dcfff",
            badge: "#3d59a1",
            session_bg: "#1a2a4a",
            icon: "⏳",
            git: true,
        },
        style,
    )
}

fn catppuccin() -> Theme {
    let mut style = BTreeMap::new();
    s(&mut style, "window-status-style", "bg=#1e1e2e,fg=#9399b2");
    s(
        &mut style,
        "window-status-current-style",
        "bg=#1e1e2e,fg=#89b4fa,bold",
    );
    s(&mut style, "pane-active-border-style", "fg=#89b4fa");
    s(&mut style, "pane-border-style", "fg=#45475a");
    s(&mut style, "message-style", "bg=#89b4fa,fg=#1e1e2e,bold");
    theme_from(
        Palette {
            bg: "#1e1e2e",
            fg: "#cdd6f4",
            dim: "#313244",
            muted: "#9399b2",
            accent: "#cba6f7",
            accent2: "#89dceb",
            badge: "#45475a",
            session_bg: "#11111b",
            icon: "◆",
            git: true,
        },
        style,
    )
}

fn dracula() -> Theme {
    let mut style = BTreeMap::new();
    s(&mut style, "window-status-style", "bg=#282a36,fg=#6272a4");
    s(
        &mut style,
        "window-status-current-style",
        "bg=#282a36,fg=#bd93f9,bold",
    );
    s(&mut style, "pane-active-border-style", "fg=#bd93f9");
    s(&mut style, "pane-border-style", "fg=#44475a");
    s(&mut style, "message-style", "bg=#ff79c6,fg=#282a36,bold");
    theme_from(
        Palette {
            bg: "#282a36",
            fg: "#f8f8f2",
            dim: "#44475a",
            muted: "#6272a4",
            accent: "#bd93f9",
            accent2: "#8be9fd",
            badge: "#44475a",
            session_bg: "#1e1f29",
            icon: "☾",
            git: true,
        },
        style,
    )
}

fn gruvbox() -> Theme {
    let mut style = BTreeMap::new();
    s(&mut style, "window-status-style", "bg=#282828,fg=#928374");
    s(
        &mut style,
        "window-status-current-style",
        "bg=#282828,fg=#fabd2f,bold",
    );
    s(&mut style, "pane-active-border-style", "fg=#fabd2f");
    s(&mut style, "pane-border-style", "fg=#504945");
    s(&mut style, "message-style", "bg=#fabd2f,fg=#282828,bold");
    theme_from(
        Palette {
            bg: "#282828",
            fg: "#ebdbb2",
            dim: "#3c3836",
            muted: "#928374",
            accent: "#fabd2f",
            accent2: "#83a598",
            badge: "#504945",
            session_bg: "#1d2021",
            icon: "",
            git: true,
        },
        style,
    )
}

fn nord() -> Theme {
    let mut style = BTreeMap::new();
    s(&mut style, "window-status-style", "bg=#2e3440,fg=#4c566a");
    s(
        &mut style,
        "window-status-current-style",
        "bg=#2e3440,fg=#88c0d0,bold",
    );
    s(&mut style, "pane-active-border-style", "fg=#88c0d0");
    s(&mut style, "pane-border-style", "fg=#3b4252");
    s(&mut style, "message-style", "bg=#88c0d0,fg=#2e3440,bold");
    theme_from(
        Palette {
            bg: "#2e3440",
            fg: "#eceff4",
            dim: "#3b4252",
            muted: "#4c566a",
            accent: "#88c0d0",
            accent2: "#8fbcbb",
            badge: "#3b4252",
            session_bg: "#242933",
            icon: "❄",
            git: true,
        },
        style,
    )
}

fn rose_pine() -> Theme {
    let mut style = BTreeMap::new();
    s(&mut style, "window-status-style", "bg=#191724,fg=#6e6a86");
    s(
        &mut style,
        "window-status-current-style",
        "bg=#191724,fg=#ebbcba,bold",
    );
    s(&mut style, "pane-active-border-style", "fg=#ebbcba");
    s(&mut style, "pane-border-style", "fg=#26233a");
    s(&mut style, "message-style", "bg=#ebbcba,fg=#191724,bold");
    theme_from(
        Palette {
            bg: "#191724",
            fg: "#e0def4",
            dim: "#26233a",
            muted: "#6e6a86",
            accent: "#ebbcba",
            accent2: "#9ccfd8",
            badge: "#403d52",
            session_bg: "#1f1d2e",
            icon: "✦",
            git: true,
        },
        style,
    )
}

fn minimal() -> Theme {
    let mut style = BTreeMap::new();
    s(
        &mut style,
        "window-status-style",
        "bg=colour234,fg=colour245",
    );
    s(
        &mut style,
        "window-status-current-style",
        "bg=colour234,fg=colour250,bold",
    );
    s(&mut style, "pane-active-border-style", "fg=colour250");
    s(&mut style, "pane-border-style", "fg=colour237");
    s(
        &mut style,
        "message-style",
        "bg=colour250,fg=colour234,bold",
    );
    theme_from(
        Palette {
            bg: "colour234",
            fg: "colour252",
            dim: "colour238",
            muted: "colour244",
            accent: "colour252",
            accent2: "colour250",
            badge: "colour237",
            session_bg: "colour233",
            icon: "●",
            git: false,
        },
        style,
    )
}
