#!/usr/bin/env python3

from datetime import datetime
from tempfile import NamedTemporaryFile
import configparser
import os
import shutil
import sys
import re
from typing import Dict, Tuple

BASE = os.path.expanduser("~/.trinity/share/config")

FILES = {
    "kateschemarc": os.path.join(BASE, "kateschemarc"),
    "katesyntaxhighlightingrc": os.path.join(BASE, "katesyntaxhighlightingrc"),
    "kwriterc": os.path.join(BASE, "kwriterc"),
    "katerc": os.path.join(BASE, "katerc"),
    "konquerorrc": os.path.join(BASE, "konquerorrc"),
    "arkrc": os.path.join(BASE, "arkrc"),
    "krusaderrc": os.path.join(BASE, "krusaderrc"),
    "konsoleschema": os.path.expanduser("~/.trinity/share/apps/konsole/Setuzuna_Timir.schema"),
    "konsolerc": os.path.join(BASE, "konsolerc"),
    "konsolepartrc": os.path.join(BASE, "konsolepartrc"),
}

WRAPPED_FILES = set()

SECTION_NAME = "setuzuna - timir"
SCHEMA_SECTION_NAME = f"Default Item Styles - Schema {SECTION_NAME}"
PYTHON_SECTION_NAME = f"Highlighting Python - Schema {SECTION_NAME}"

PALETTE: Dict[str, str] = {
    # core
    "WHITE":      "ffffff",
    "FOREGROUND": "dfdedc",
    "BACKGROUND": "1b1e20",
    "BRACKET_HL": "000000",
    "LINE_HL":    "2a2e32",
    "ICON_BAR":   "222528",
    "LINENUM":    "b0b2b9",
    "REGION_BG":  "40464d",

    # marks / syntax
    "MARK1":       "56b6c2",
    "MARK2":       "e06c75",
    "MARK3":       "e5c07b",
    "MARK4":       "bf86ff",
    "MARK5":       "8091a1",
    "MARK6":       "98c379",

    # selection/tab/wrap
    "SELECTION":   "184880",
    "TAB_MARKER":  "3c4252",

    # syntax-specific
    "ALERT":       "eb6b6b",
    "COMMENT":     "5c6370",
    "DATATYPE":    "61afef",
    "STRING":      "f9ca7b",
    "NUMBER":      "81c784",
    "KEYWORD":     "c792ea",
    "FUNCTION":    "f3e88d",
    "DECORATOR":   "8f6b32",
    "SPECIAL":     "0057ae",

    # misc
    "MID_GREY":    "686868",
    "BRIGHT_RED":  "ff5454",
    "BRIGHT_GRN":  "54ff54",
    "BRIGHT_YLW":  "ffff54",
    "BRIGHT_BLU":  "5454ff",
    "BRIGHT_MAG":  "ff54ff",
    "BRIGHT_CYN":  "54ffff",
}

KATE_DOCUMENT_DEFAULTS = {
    "Basic Config Flags": "583565346",
    "Indentation Mode": "0",
    "Indentation Width": "4",
    "Tab Width": "4",
}

KATE_RENDERER_DEFAULTS = {
    "Schema": SECTION_NAME,
    "Show Indentation Lines": "true",
    "Word Wrap Marker": "false",
}

KATE_VIEW_DEFAULTS = {
    "Line Numbers": "true",
    "Icon Bar": "false",
    "Folding Bar": "true",
}

globals().update({k: v for k, v in PALETTE.items()})

def hex_to_rgb_tuple(h: str) -> Tuple[int, int, int]:
    return int(h[0:2], 16), int(h[2:4], 16), int(h[4:6], 16)

def hex_to_rgb_str(hex_or_key: str, sep: str = ",") -> str:
    r, g, b = hex_to_rgb_tuple(hex_or_key)
    return f"{r}{sep}{g}{sep}{b}"

def ff_hex(h: str) -> str:
    return f"ff{h}"

def build_kate_schema_entries() -> Dict[str, str]:
    return {
        "Color Background":          hex_to_rgb_str(BACKGROUND),
        "Color Highlighted Bracket": hex_to_rgb_str(BRACKET_HL),
        "Color Highlighted Line":    hex_to_rgb_str(LINE_HL),
        "Color Icon Bar":            hex_to_rgb_str(ICON_BAR),
        "Color Line Number":         hex_to_rgb_str(LINENUM),
        "Color MarkType1":           hex_to_rgb_str(MARK1),
        "Color MarkType2":           hex_to_rgb_str(MARK2),
        "Color MarkType3":           hex_to_rgb_str(MARK3),
        "Color MarkType4":           hex_to_rgb_str(MARK4),
        "Color MarkType5":           hex_to_rgb_str(MARK5),
        "Color MarkType6":           hex_to_rgb_str(MARK6),
        "Color MarkType7":           hex_to_rgb_str(MARK2),
        "Color Selection":           hex_to_rgb_str(SELECTION),
        "Color Tab Marker":          hex_to_rgb_str(TAB_MARKER),
        "Color Word Wrap Marker":    hex_to_rgb_str(TAB_MARKER),
        "Font":                      "Droid Sans Mono,9,-1,5,50,0,0,0,0,0",
    }

def build_syntax_style_entries() -> Dict[str, str]:
    entries = {
        "Alert":           [ff_hex(WHITE), ff_hex(ALERT), "1", "", "", "", ff_hex(ALERT), "-", "---"],
        "Base-N Integer":  [ff_hex(NUMBER), ff_hex(WHITE), "", "", "", "", "-", "-", "---"],
        "Character":       [ff_hex(STRING), ff_hex(WHITE), "", "", "", "", "-", "-", "---"],
        "Comment":         [ff_hex(COMMENT), ff_hex(WHITE), "", "1", "", "", "", "-", "---"],
        "Data Type":       [ff_hex(DATATYPE), ff_hex(WHITE), "", "", "", "", "-", "-", "---"],
        "Decimal/Value":   [ff_hex(NUMBER), ff_hex(WHITE), "", "", "", "", "-", "-", "---"],
        "Error":           [ff_hex(WHITE), ff_hex(ALERT), "", "", "", "1", "-", "-", "---"],
        "Floating Point":  [ff_hex(NUMBER), ff_hex(WHITE), "", "", "", "", "-", "-", "---"],
        "Function":        [ff_hex(FUNCTION), ff_hex(WHITE), "", "", "", "", "-", "-", "---"],
        "Keyword":         [ff_hex(KEYWORD), ff_hex(WHITE), "1", "", "", "", "-", "-", "---"],
        "Normal":          [ff_hex(FOREGROUND), ff_hex(WHITE), "", "", "", "", "-", "-", "---"],
        "Others":          [ff_hex(DATATYPE), ff_hex(WHITE), "", "", "", "", "-", "-", "---"],
        "Region Marker":   [ff_hex(WHITE), ff_hex(REGION_BG), "", "", "", "", ff_hex(REGION_BG), "-", "---"],
        "String":          [ff_hex(STRING), ff_hex(WHITE), "", "", "", "", "-", "-", "---"],
    }
    return {k: ",".join(v) for k, v in entries.items()}

def build_python_style_entries() -> Dict[str, str]:
    entries = {
        "Alerts:Alert":                ["9",  ff_hex(ALERT), ff_hex(ALERT), "", "", "", "", "", "0", "---"],
        "Alerts:Normal Text":          ["0",  ff_hex(FOREGROUND), ff_hex(WHITE), "", "", "", "", "", "0", "---"],
        "Alerts_indent:Normal Text":   ["0",  ff_hex(FOREGROUND), ff_hex(WHITE), "", "", "", "", "", "0", "---"],
        "Modelines:Comment":           ["8",  ff_hex(COMMENT), ff_hex(WHITE), "", "", "", "", "", "0", "---"],
        "Modelines:Keyword":           ["1",  ff_hex(KEYWORD), ff_hex(WHITE), "", "", "", "", "", "0", "---"],
        "Modelines:Number":            ["3",  ff_hex(NUMBER), ff_hex(WHITE), "", "", "", "", "", "0", "---"],
        "Modelines:Option OFF":        ["9",  ff_hex(WHITE), ff_hex(WHITE), "", "", "", "", "", "0", "---"],
        "Modelines:Option ON":         ["9",  ff_hex(WHITE), ff_hex(WHITE), "", "", "", "", "", "0", "---"],
        "Modelines:String":            ["7",  ff_hex(STRING), ff_hex(WHITE), "", "", "", "", "", "0", "---"],
        "Modelines:Value":             ["5",  ff_hex(NUMBER), ff_hex(WHITE), "", "", "", "", "", "0", "---"],
        "Modelines:Variable":          ["11", ff_hex(DATATYPE), ff_hex(WHITE), "", "", "", "", "", "0", "---"],
        "Python:Builtin Function":     ["2",  ff_hex(FUNCTION), ff_hex(WHITE), "", "", "", "", "", "0", "---"],
        "Python:Command Keyword":      ["1",  ff_hex(KEYWORD), ff_hex(WHITE), "", "", "", "", "", "0", "---"],
        "Python:Comment":              ["8",  ff_hex(COMMENT), ff_hex(WHITE), "", "", "", "", "", "0", "---"],
        "Python:Complex":              ["9",  ff_hex(FOREGROUND), ff_hex(WHITE), "", "", "", "", "", "0", "---"],
        "Python:Decorator":            ["9",  ff_hex(DECORATOR), ff_hex(WHITE), "", "", "", "", "", "0", "---"],
        "Python:Definition Keyword":   ["1",  ff_hex(KEYWORD), ff_hex(WHITE), "", "", "", "", "", "0", "---"],
        "Python:Exceptions":           ["9",  ff_hex(ALERT), ff_hex(ALERT), "1", "", "", "", "", "0", "---"],
        "Python:Extensions":           ["9",  ff_hex(DATATYPE), ff_hex(WHITE), "1", "", "", "", "", "0", "---"],
        "Python:Float":                ["5",  ff_hex(NUMBER), ff_hex(WHITE), "", "", "", "", "", "0", "---"],
        "Python:Flow Control Keyword": ["1",  ff_hex(KEYWORD), ff_hex(WHITE), "", "", "", "", "", "0", "---"],
        "Python:Hex":                  ["4",  ff_hex(NUMBER), ff_hex(WHITE), "", "", "", "", "", "0", "---"],
        "Python:Int":                  ["3",  ff_hex(NUMBER), ff_hex(WHITE), "", "", "", "", "", "0", "---"],
        "Python:Normal Text":          ["0",  ff_hex(FOREGROUND), ff_hex(WHITE), "", "", "", "", "", "0", "---"],
        "Python:Octal":                ["4",  ff_hex(NUMBER), ff_hex(WHITE), "", "", "", "", "" ,"0", "---"],
        "Python:Operator":             ["0",  ff_hex(DATATYPE), ff_hex(WHITE), "1", "", "", "", "", "0", "---"],
        "Python:Overloaders":          ["9",  ff_hex(DATATYPE), ff_hex(WHITE), "1", "", "", "", "", "0", "---"],
        "Python:Preprocessor":         ["6",  ff_hex(DATATYPE), ff_hex(WHITE), "", "", "", "", "", "0", "---"],
        "Python:Raw String":           ["7",  ff_hex(STRING), ff_hex(WHITE), "", "", "", "", "", "0", "---"],
        "Python:Special Variable":     ["9",  ff_hex(DATATYPE), ff_hex(WHITE), "", "", "", "", "", "0", "---"],
        "Python:String":               ["7",  ff_hex(STRING), ff_hex(WHITE), "", "", "", "", "", "0", "---"],
        "Python:String Char":          ["6",  ff_hex(STRING), ff_hex(WHITE), "", "", "", "", "", "0", "---"],
        "Python:String Substitution":  ["9",  ff_hex(SPECIAL), ff_hex(WHITE), "", "", "", "", "", "0", "---"],
    }
    return {k: ",".join(v) for k, v in entries.items()}

DEFAULT_KONSOLE_INDEX_MAP = {
    0:  (FOREGROUND, 0, 0),
    1:  (BACKGROUND, 1, 0),
    2:  (BACKGROUND, 0, 0),
    3:  (MARK2, 0, 0),
    4:  (MARK6, 0, 0),
    5:  (MARK3, 0, 0),
    6:  (SPECIAL, 0, 0),
    7:  (MARK4, 0, 0),
    8:  (MARK1, 0, 0),
    9:  (LINENUM, 0, 0),
    10: (WHITE, 0, 0),
    11: (MID_GREY, 1, 0),
    12: (MID_GREY, 0, 0),
    13: (BRIGHT_RED, 0, 0),
    14: (BRIGHT_GRN, 0, 0),
    15: (BRIGHT_YLW, 0, 0),
    16: (BRIGHT_BLU, 0, 0),
    17: (BRIGHT_MAG, 0, 0),
    18: (BRIGHT_CYN, 0, 0),
    19: (WHITE, 0, 0),
}

def generate_konsole_block() -> str:
    lines = []
    lines.append("title Setuzuna - Timir")
    lines.append("transparency 0.75 0 0 0")
    for i in range(20):
        key, flag1, flag2 = DEFAULT_KONSOLE_INDEX_MAP[i]
        rgb = hex_to_rgb_str(key, sep=" ")
        lines.append(f"color {i:2d} {rgb}  {flag1} {flag2}")
    return "\n".join(lines)

def backup_file(path):
    if not os.path.exists(path):
        return None
    dirname = os.path.dirname(path)
    base = os.path.basename(path)
    ts = datetime.now().strftime("%Y%m%dT%H%M%S")
    bak = os.path.join(dirname, f"{base}.bak.{ts}")
    shutil.copy2(path, bak)
    return bak

def load_config(path):
    parser = configparser.RawConfigParser(strict=False)
    parser.optionxform = str
    if not os.path.exists(path):
        return parser

    try:
        with open(path, "r", encoding="utf-8") as f:
            parser.read_file(f)
        return parser
    except configparser.MissingSectionHeaderError:
        with open(path, "r", encoding="utf-8") as f:
            content = f.read()
        wrapped = "[__root__]\n" + content
        parser.read_string(wrapped)
        WRAPPED_FILES.add(path)
        return parser
    except Exception:
        try:
            parser.read(path, encoding="utf-8")
        except Exception:
            return configparser.RawConfigParser(strict=False)
        return parser

def remove_dummy_header(path):
    try:
        with open(path, "r", encoding="utf-8") as f:
            lines = f.readlines()
    except Exception:
        return False

    idx = 0
    while idx < len(lines) and lines[idx].strip() == "":
        idx += 1
    while idx < len(lines) and lines[idx].lstrip().startswith(("#", ";")):
        idx += 1

    if idx < len(lines):
        first = lines[idx].lstrip("\ufeff").strip()
        if first == "[__root__]":
            del lines[idx]
            dirn = os.path.dirname(path) or "."
            with NamedTemporaryFile("w", encoding="utf-8", delete=False, dir=dirn) as tf:
                tf.writelines(lines)
                tmpname = tf.name
            os.replace(tmpname, path)
            return True
    return False

def safe_write_config(parser, path):
    dirn = os.path.dirname(path) or "."
    os.makedirs(dirn, exist_ok=True)
    with NamedTemporaryFile("w", encoding="utf-8", delete=False, dir=dirn) as tf:
        parser.write(tf)
        tmpname = tf.name
    os.replace(tmpname, path)

    if path in WRAPPED_FILES:
        try:
            removed = remove_dummy_header(path)
        finally:
            WRAPPED_FILES.discard(path)

def replace_section_with_entries(path, section_name, entries):
    parser = load_config(path)
    removed = False
    if parser.has_section(section_name):
        parser.remove_section(section_name)
        removed = True
    if not parser.has_section(section_name):
        parser.add_section(section_name)
    for k, v in entries.items():
        parser.set(section_name, k, v)
    bak = backup_file(path)
    safe_write_config(parser, path)
    return bak, removed

def update_multiple_sections(path, sections_entries):
    parser = load_config(path)
    prev_values = {}
    created_sections = {}
    for section, entries in sections_entries.items():
        created = False
        if not parser.has_section(section):
            parser.add_section(section)
            created = True
        if parser.has_option(section, "Schema"):
            prev_values[section] = parser.get(section, "Schema")
        else:
            prev_values[section] = None
        for k, v in entries.items():
            parser.set(section, k, v)
        created_sections[section] = created

    bak = backup_file(path)
    safe_write_config(parser, path)
    return bak, created_sections, prev_values

def set_kate_defaults(path):
    sections = {
        "Kate Document Defaults": KATE_DOCUMENT_DEFAULTS,
        "Kate Renderer Defaults": KATE_RENDERER_DEFAULTS,
        "Kate View Defaults": KATE_VIEW_DEFAULTS,
    }
    return update_multiple_sections(path, sections)

def install_konsolerc(path: str) -> Tuple[str, Dict[str, str], bool]:
    parser = load_config(path)
    created = False
    prev = {}

    section = "Desktop Entry"
    if not parser.has_section(section):
        parser.add_section(section)
        created = True

    for key in ("EncodingName", "schema"):
        prev[key] = parser.get(section, key) if parser.has_option(section, key) else None

    parser.set(section, "EncodingName", "utf8")
    parser.set(section, "defaultfont", "Noto Mono,8,-1,5,50,0,0,0,0,0")
    parser.set(section, "schema", "Setuzuna_Timir.schema")
    parser.set(section, "history", "0")
    
    

    bak = backup_file(path)
    safe_write_config(parser, path)
    return bak, prev, created

def install_konsolepartrc(path: str) -> Tuple[str, Dict[str, str], bool]:
    parser = load_config(path)
    created = False
    prev = {}

    section = "Desktop Entry"
    if not parser.has_section(section):
        parser.add_section(section)
        created = True

    prev_key = "use_konsole_settings"
    prev[prev_key] = parser.get(section, prev_key) if parser.has_option(section, prev_key) else None

    parser.set(section, prev_key, "true")

    bak = backup_file(path)
    safe_write_config(parser, path)
    return bak, prev, created

if __name__ == "__main__":
    changes = []

    kate_entries = build_kate_schema_entries()
    syntax_entries = build_syntax_style_entries()
    python_entries = build_python_style_entries()
    konsole_text = generate_konsole_block()

    bak1, removed1 = replace_section_with_entries(FILES["kateschemarc"], SECTION_NAME, kate_entries)
    changes.append(("kateschemarc", FILES["kateschemarc"], bak1, removed1))

    bak2a, removed2a = replace_section_with_entries(FILES["katesyntaxhighlightingrc"], SCHEMA_SECTION_NAME, syntax_entries)
    changes.append(("katesyntaxhighlightingrc:Default", FILES["katesyntaxhighlightingrc"], bak2a, removed2a))

    bak2b, removed2b = replace_section_with_entries(FILES["katesyntaxhighlightingrc"], PYTHON_SECTION_NAME, python_entries)
    changes.append(("katesyntaxhighlightingrc:Python", FILES["katesyntaxhighlightingrc"], bak2b, removed2b))

    for name in ("kwriterc", "katerc", "konquerorrc", "arkrc", "krusaderrc"):
        path = FILES[name]
        bak, created, prev = set_kate_defaults(path)
        changes.append((name, path, bak, created, prev))

    try:
        konsole_dir = os.path.dirname(FILES["konsoleschema"])
        os.makedirs(konsole_dir, exist_ok=True)
        with open(FILES["konsoleschema"], "w", encoding="utf-8") as f:
            f.write(konsole_text)
        konsole_bak = None
    except Exception as e:
        konsole_bak = f"ERROR_WRITING:{e}"

    try:
        bak_konsolerc, prev_konsolerc, created_konsolerc = install_konsolerc(FILES["konsolerc"])
        changes.append(("konsolerc", FILES["konsolerc"], bak_konsolerc, created_konsolerc, prev_konsolerc))
    except Exception as e:
        changes.append(("konsolerc", FILES["konsolerc"], f"ERROR:{e}"))

    try:
        bak_konsolepartrc, prev_konsolepartrc, created_konsolepartrc = install_konsolepartrc(FILES["konsolepartrc"])
        changes.append(("konsolepartrc", FILES["konsolepartrc"], bak_konsolepartrc, created_konsolepartrc, prev_konsolepartrc))
    except Exception as e:
        changes.append(("konsolepartrc", FILES["konsolepartrc"], f"ERROR:{e}"))

    print("Update summary:")
    for item in changes:
        print(" -", item)
    print(f"\nKonsole theme written to: {FILES['konsoleschema']} (or error: {konsole_bak})")
    print("\nDone. Backups were created next to original files (if the files existed).")
