#!/usr/bin/env python3

from __future__ import annotations

import json
from pathlib import Path
from textwrap import dedent


ROOT = Path(__file__).resolve().parent
PALETTE_FILE = ROOT / "palette.json"


def load_palette() -> dict[str, str]:
    data = json.loads(PALETTE_FILE.read_text())
    return data["colors"]


def hex_no_hash(value: str) -> str:
    return value.lstrip("#")


def hex_to_rgb(value: str) -> tuple[int, int, int]:
    value = hex_no_hash(value)
    return tuple(int(value[index:index + 2], 16) for index in (0, 2, 4))


def camel_from_snake(name: str) -> str:
    head, *tail = name.split("_")
    return head + "".join(part.capitalize() for part in tail)


def write(path: Path, content: str) -> None:
    path.write_text(content.rstrip() + "\n")


def sgr_fg(value: str, *, bold: bool = False) -> str:
    red, green, blue = hex_to_rgb(value)
    parts = []
    if bold:
        parts.append("1")
    parts.append(f"38;2;{red};{green};{blue}")
    return ";".join(parts)


def build_ls_colors(colors: dict[str, str]) -> str:
    entries = {
        "no": "0",
        "fi": "0",
        "di": sgr_fg(colors["func"], bold=True),
        "ln": sgr_fg(colors["field"], bold=True),
        "or": sgr_fg(colors["error"], bold=True),
        "mi": "0",
        "so": sgr_fg(colors["type"]),
        "pi": sgr_fg(colors["string"]),
        "do": sgr_fg(colors["func"], bold=True),
        "bd": sgr_fg(colors["number"]),
        "cd": sgr_fg(colors["number"]),
        "su": sgr_fg(colors["error"], bold=True),
        "sg": sgr_fg(colors["escape"], bold=True),
        "ex": sgr_fg(colors["func"], bold=True),
        "*.tar": sgr_fg(colors["string"]),
        "*.tgz": sgr_fg(colors["string"]),
        "*.gz": sgr_fg(colors["string"]),
        "*.bz2": sgr_fg(colors["string"]),
        "*.xz": sgr_fg(colors["string"]),
        "*.zip": sgr_fg(colors["string"]),
        "*.7z": sgr_fg(colors["string"]),
        "*.zst": sgr_fg(colors["string"]),
        "*.rar": sgr_fg(colors["string"]),
        "*.jpg": sgr_fg(colors["field"]),
        "*.jpeg": sgr_fg(colors["field"]),
        "*.png": sgr_fg(colors["field"]),
        "*.gif": sgr_fg(colors["field"]),
        "*.svg": sgr_fg(colors["field"]),
        "*.webp": sgr_fg(colors["field"]),
        "*.mp3": sgr_fg(colors["type"]),
        "*.flac": sgr_fg(colors["type"]),
        "*.wav": sgr_fg(colors["type"]),
        "*.mp4": sgr_fg(colors["type"]),
        "*.mkv": sgr_fg(colors["type"]),
        "*.mov": sgr_fg(colors["type"]),
        "*.pdf": sgr_fg(colors["number"]),
        "*.md": sgr_fg(colors["comment"]),
        "*.txt": sgr_fg(colors["fg"]),
        "*.log": sgr_fg(colors["comment"]),
        "*.conf": sgr_fg(colors["keyword"]),
        "*.json": sgr_fg(colors["keyword"]),
        "*.yaml": sgr_fg(colors["keyword"]),
        "*.yml": sgr_fg(colors["keyword"]),
        "*.toml": sgr_fg(colors["keyword"]),
        "*.ini": sgr_fg(colors["keyword"]),
        "*.sh": sgr_fg(colors["func"], bold=True),
        "*.bash": sgr_fg(colors["func"], bold=True),
        "*.zsh": sgr_fg(colors["func"], bold=True),
        "*.py": sgr_fg(colors["func"]),
        "*.js": sgr_fg(colors["keyword"]),
        "*.ts": sgr_fg(colors["keyword"]),
        "*.tsx": sgr_fg(colors["keyword"]),
        "*.jsx": sgr_fg(colors["keyword"]),
        "*.lua": sgr_fg(colors["type_alt"]),
        "*.rs": sgr_fg(colors["type_alt"]),
        "*.go": sgr_fg(colors["type_alt"]),
        "*.c": sgr_fg(colors["field"]),
        "*.h": sgr_fg(colors["field"]),
        "*.cpp": sgr_fg(colors["field"]),
        "*.hpp": sgr_fg(colors["field"]),
    }
    return ":".join(f"{key}={value}" for key, value in entries.items())


def render_palette_sh(colors: dict[str, str]) -> str:
    lines = [
        "# Generated from palette.json by generate.py. Do not edit directly.",
        "",
    ]
    for name, value in colors.items():
        lines.append(f'export RIDER_{name.upper()}="{value}"')
    lines.append("")
    lines.append("# Shared Rider palette for GNU ls and compatible tools.")
    lines.append(f'export LS_COLORS="{build_ls_colors(colors)}"')
    return "\n".join(lines)


def render_palette_css(colors: dict[str, str]) -> str:
    lines = [
        "/* Generated from palette.json by generate.py. Do not edit directly. */",
    ]
    for name, value in colors.items():
        lines.append(f"@define-color rider-{name.replace('_', '-')} {value};")
    return "\n".join(lines)


def render_palette_hypr(colors: dict[str, str]) -> str:
    lines = [
        "# Generated from palette.json by generate.py. Do not edit directly.",
        "# Usage:",
        "#   source = ~/.config/rider-palette/palette.hyprland.conf",
        "",
    ]
    for name, value in colors.items():
        lines.append(f"${name} = rgb({hex_no_hash(value)})")
        lines.append(f"${camel_from_snake(name)}Alpha = {hex_no_hash(value)}")
    return "\n".join(lines)


def render_palette_rasi(colors: dict[str, str]) -> str:
    lines = [
        "/* Generated from palette.json by generate.py. Do not edit directly. */",
        "* {",
    ]
    for name, value in colors.items():
        lines.append(f"  rider-{name.replace('_', '-')}: {value};")
    lines.append("}")
    return "\n".join(lines)


def render_tmux_conf(colors: dict[str, str]) -> str:
    return dedent(
        f"""
        # Generated from palette.json by generate.py. Do not edit directly.
        # Source this near the end of ~/.tmux.conf.

        set -g status-style "bg={colors['bg']},fg={colors['fg_bright']}"
        set -g status-left-style "bg={colors['bg']},fg={colors['fg_bright']}"
        set -g status-right-style "bg={colors['bg']},fg={colors['fg_bright']}"
        set -g status-left-length 48
        set -g status-right-length 80
        set -g status-justify centre
        set -g window-status-separator " "
        set -g status-left "#[fg={colors['bg']},bg={colors['func']},bold] #S #[fg={colors['func']},bg={colors['bg']}]"
        set -g status-right "#[fg={colors['field']},bg={colors['bg']}]#[fg={colors['bg']},bg={colors['field']}] %Y-%m-%d #[fg={colors['string']},bg={colors['field']}]#[fg={colors['bg']},bg={colors['string']}] %H:%M#[fg={colors['string']},bg={colors['bg']}]"

        set -g message-style "bg={colors['cursor_line']},fg={colors['fg_bright']}"
        set -g message-command-style "bg={colors['cursor_line']},fg={colors['fg_bright']}"
        set -g mode-style "bg={colors['selection']},fg={colors['fg_bright']}"

        set -g pane-border-style "fg={colors['border']}"
        set -g pane-active-border-style "fg={colors['keyword']}"
        set -g clock-mode-colour "{colors['keyword']}"

        set -g window-status-style "bg={colors['bg']},fg={colors['fg_gutter']}"
        set -g window-status-current-style "bg={colors['bg']},fg={colors['fg_bright']}"
        set -g window-status-current-format "#[fg={colors['func']},bg={colors['bg']}]#[fg={colors['bg']},bg={colors['func']},bold] #I #[fg={colors['func']},bg={colors['border']}]#[fg={colors['fg_bright']},bg={colors['border']}] #W #[fg={colors['border']},bg={colors['bg']}]"
        set -g window-status-format "#[fg={colors['cursor_line']},bg={colors['bg']}]#[fg={colors['fg_gutter']},bg={colors['cursor_line']}] #I #[fg={colors['cursor_line']},bg={colors['border']}]#[fg={colors['fg_bright']},bg={colors['border']}] #W #[fg={colors['border']},bg={colors['bg']}]"
        """
    ).strip()


def render_alacritty_toml(colors: dict[str, str]) -> str:
    return dedent(
        f"""
        # Generated from palette.json by generate.py. Do not edit directly.

        [colors.primary]
        background = "{colors['bg']}"
        foreground = "{colors['fg']}"
        dim_foreground = "{colors['fg_gutter']}"
        bright_foreground = "{colors['fg_bright']}"

        [colors.cursor]
        text = "{colors['bg']}"
        cursor = "{colors['fg_bright']}"

        [colors.vi_mode_cursor]
        text = "{colors['bg']}"
        cursor = "{colors['keyword']}"

        [colors.selection]
        text = "{colors['fg_bright']}"
        background = "{colors['selection']}"

        [colors.search.matches]
        foreground = "{colors['fg_bright']}"
        background = "{colors['border']}"

        [colors.search.focused_match]
        foreground = "{colors['bg']}"
        background = "{colors['func']}"

        [colors.hints.start]
        foreground = "{colors['bg']}"
        background = "{colors['string']}"

        [colors.hints.end]
        foreground = "{colors['fg_bright']}"
        background = "{colors['cursor_line']}"

        [colors.line_indicator]
        foreground = "None"
        background = "None"

        [colors.footer_bar]
        foreground = "{colors['fg_bright']}"
        background = "{colors['bg']}"

        [colors.normal]
        black = "{colors['bg']}"
        red = "{colors['error']}"
        green = "{colors['func']}"
        yellow = "{colors['string']}"
        blue = "{colors['keyword']}"
        magenta = "{colors['type']}"
        cyan = "{colors['field']}"
        white = "{colors['fg']}"

        [colors.bright]
        black = "{colors['border']}"
        red = "{colors['error']}"
        green = "{colors['comment']}"
        yellow = "{colors['string']}"
        blue = "{colors['keyword']}"
        magenta = "{colors['type_alt']}"
        cyan = "{colors['field']}"
        white = "{colors['fg_bright']}"

        [colors.dim]
        black = "{colors['cursor_line']}"
        red = "{colors['error']}"
        green = "{colors['comment']}"
        yellow = "{colors['string']}"
        blue = "{colors['keyword']}"
        magenta = "{colors['type']}"
        cyan = "{colors['field']}"
        white = "{colors['fg_gutter']}"
        """
    ).strip()


def render_bash_prompt() -> str:
    return dedent(
        """
        # Generated from palette.json by generate.py. Do not edit directly.
        # Rider-colored bash prompt using the shared palette.

        # shellcheck shell=bash

        if [ -r "$HOME/.config/rider-palette/palette.sh" ]; then
          . "$HOME/.config/rider-palette/palette.sh"
        fi

        rider_fg() {
          local hex="${1#\\#}"
          printf '\\[\\033[38;2;%d;%d;%dm\\]' "$((16#${hex:0:2}))" "$((16#${hex:2:2}))" "$((16#${hex:4:2}))"
        }

        RIDER_RESET='\\[\\033[0m\\]'

        __rider_git_branch() {
          command -v git >/dev/null 2>&1 || return 0
          git rev-parse --is-inside-work-tree >/dev/null 2>&1 || return 0

          local branch
          branch="$(git symbolic-ref --quiet --short HEAD 2>/dev/null || git rev-parse --short HEAD 2>/dev/null)" || return 0
          [ -n "$branch" ] || return 0

          printf ' %sgit:%s%s' "$(rider_fg "$RIDER_FUNC")" "$branch" "$RIDER_RESET"
        }

        __rider_set_bash_prompt() {
          PS1="${debian_chroot:+($debian_chroot)}$(rider_fg "$RIDER_FG_BRIGHT")\\u@\\h${RIDER_RESET}$(rider_fg "$RIDER_BORDER"):$(rider_fg "$RIDER_KEYWORD")\\w${RIDER_RESET}$(__rider_git_branch)$(rider_fg "$RIDER_BORDER") \\\\$ ${RIDER_RESET}"
        }

        PROMPT_COMMAND=__rider_set_bash_prompt
        """
    ).strip()


def render_p10k_zsh() -> str:
    return dedent(
        """
        # Generated from palette.json by generate.py. Do not edit directly.
        # Rider-colored Powerlevel10k overrides using the shared palette.

        [[ -r "$HOME/.config/rider-palette/palette.sh" ]] && source "$HOME/.config/rider-palette/palette.sh"

        typeset -g POWERLEVEL9K_BACKGROUND="$RIDER_BG"

        typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_PREFIX="%F{$RIDER_BORDER}╭─%f"
        typeset -g POWERLEVEL9K_MULTILINE_NEWLINE_PROMPT_PREFIX="%F{$RIDER_BORDER}├─%f"
        typeset -g POWERLEVEL9K_MULTILINE_LAST_PROMPT_PREFIX="%F{$RIDER_BORDER}╰─%f"
        typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_SUFFIX="%F{$RIDER_BORDER}─╮%f"
        typeset -g POWERLEVEL9K_MULTILINE_NEWLINE_PROMPT_SUFFIX="%F{$RIDER_BORDER}─┤%f"
        typeset -g POWERLEVEL9K_MULTILINE_LAST_PROMPT_SUFFIX="%F{$RIDER_BORDER}─╯%f"

        typeset -g POWERLEVEL9K_LEFT_SUBSEGMENT_SEPARATOR="%F{$RIDER_BORDER}\\uE0B1%f"
        typeset -g POWERLEVEL9K_RIGHT_SUBSEGMENT_SEPARATOR="%F{$RIDER_BORDER}\\uE0B3%f"

        typeset -g POWERLEVEL9K_OS_ICON_FOREGROUND="$RIDER_FG_BRIGHT"

        typeset -g POWERLEVEL9K_DIR_FOREGROUND="$RIDER_KEYWORD"
        typeset -g POWERLEVEL9K_DIR_SHORTENED_FOREGROUND="$RIDER_TYPE"
        typeset -g POWERLEVEL9K_DIR_ANCHOR_FOREGROUND="$RIDER_FUNC"

        typeset -g POWERLEVEL9K_VCS_VISUAL_IDENTIFIER_COLOR="$RIDER_FUNC"
        typeset -g POWERLEVEL9K_VCS_LOADING_VISUAL_IDENTIFIER_COLOR="$RIDER_BORDER"
        typeset -g POWERLEVEL9K_VCS_CLEAN_FOREGROUND="$RIDER_FUNC"
        typeset -g POWERLEVEL9K_VCS_UNTRACKED_FOREGROUND="$RIDER_FIELD"
        typeset -g POWERLEVEL9K_VCS_MODIFIED_FOREGROUND="$RIDER_NUMBER"

        typeset -g POWERLEVEL9K_STATUS_OK_FOREGROUND="$RIDER_FUNC"
        typeset -g POWERLEVEL9K_STATUS_OK_PIPE_FOREGROUND="$RIDER_FUNC"
        typeset -g POWERLEVEL9K_STATUS_ERROR_FOREGROUND="$RIDER_ERROR"
        typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL_FOREGROUND="$RIDER_ERROR"
        typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE_FOREGROUND="$RIDER_ERROR"

        typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_FOREGROUND="$RIDER_STRING"
        typeset -g POWERLEVEL9K_TIME_FOREGROUND="$RIDER_FIELD"
        typeset -g POWERLEVEL9K_DOTNET_VERSION_FOREGROUND="$RIDER_TYPE"
        typeset -g POWERLEVEL9K_CONTEXT_FOREGROUND="$RIDER_FG_BRIGHT"
        """
    ).strip()


def render_readme() -> str:
    return dedent(
        """
        # Rider Palette Reuse

        `palette.json` is the single source of truth for your shared Rider palette.

        Regenerate all derived files with:

        ```bash
        python3 ~/.config/rider-palette/generate.py
        ```

        Generated outputs:

        - `palette.sh`: shell env vars plus shared `LS_COLORS` for prompts and scripts
        - `palette.css`: CSS variables for Waybar and Wofi
        - `palette.hyprland.conf`: Hyprlang variables for Hyprland and Hyprlock
        - `palette.rasi`: Rasi variables for Rofi
        - `tmux.conf`: tmux status and pane colors
        - `alacritty.toml`: Alacritty color theme
        - `bash-prompt.sh`: bash prompt using the shared palette
        - `p10k.zsh`: Powerlevel10k overrides using the shared palette

        This package is meant to be stowed from the dotfiles repo so that
        `~/.config/rider-palette/*` becomes available to the rest of the system.
        """
    ).strip()


def main() -> None:
    colors = load_palette()

    outputs = {
        ROOT / "palette.sh": render_palette_sh(colors),
        ROOT / "palette.css": render_palette_css(colors),
        ROOT / "palette.hyprland.conf": render_palette_hypr(colors),
        ROOT / "palette.rasi": render_palette_rasi(colors),
        ROOT / "tmux.conf": render_tmux_conf(colors),
        ROOT / "alacritty.toml": render_alacritty_toml(colors),
        ROOT / "bash-prompt.sh": render_bash_prompt(),
        ROOT / "p10k.zsh": render_p10k_zsh(),
        ROOT / "README.md": render_readme(),
    }

    for path, content in outputs.items():
        write(path, content)


if __name__ == "__main__":
    main()
