import re
from datetime import datetime as dt
from difflib import unified_diff
from functools import partial
from types import SimpleNamespace
from typing import Any, Callable


RESET = "\033[0m"


def ID(text: str) -> str:
    return text


def is_colorized(text: str) -> bool:
    return RESET in text


def colorize(text: str, color: str) -> str:
    return f"{color}{text}{RESET}"


_colors: dict[str, Callable[[str], str]] = dict(
    red=partial(colorize, color="\033[31m"),
    green=partial(colorize, color="\033[32m"),
    cyan=partial(colorize, color="\033[36m"),
    magenta=partial(colorize, color="\033[35m"),
    yellow=partial(colorize, color="\033[33m"),
    gray=partial(colorize, color="\033[90m"),
    bold=partial(colorize, color="\033[1m"),
)

COL: Any = SimpleNamespace(**_colors)
COL_DUMMY: Any = SimpleNamespace(**{k: ID for k in _colors.keys()})


def get_colorer(color: bool):
    return COL if color else COL_DUMMY


def show_all_colors():
    for name, color_fn in _colors.items():
        print(color_fn(name.upper()))
    print(RESET + "RESET")


def trim_last_column(tbl):
    res = re.sub(r"(?:[-\s])+[|]([\n]|$)", "\n", tbl)
    return res


def _colorize_diff_line(line: str, col) -> str:
    if line.startswith("+") and not line.startswith("+++"):
        line_fix = col.green(line)
    elif line.startswith("-") and not line.startswith("---"):
        line_fix = col.red(line)
    elif line.startswith("---") or line.startswith("+++"):
        line_fix = col.bold(line)
    else:
        line_fix = line
    return line_fix if isinstance(line_fix, str) else line


def build_diff(text_old, text, *, path_old=None, path=None, col=None) -> str:
    col = col or COL_DUMMY
    if text != text_old:
        old_lines = text_old.strip().splitlines(keepends=True)
        new_lines = text.strip().splitlines(keepends=True)

        diff = unified_diff(
            old_lines,
            new_lines,
            fromfile=path_old and str(path_old) or "",
            tofile=path and str(path) or "",
            lineterm="",  # prevent extra newlines
        )

        colored = [_colorize_diff_line(line.strip(), col) + "\n" for line in diff]
        changes_msg = col.yellow("Changes:")
        return str(changes_msg) + "\n" + "".join(colored)
    else:
        no_changes_msg = col.yellow("No changes")
        return f"{str(no_changes_msg)}. Actual version at {path}"


def _make_debug(msg):
    if is_colorized(msg):
        return msg
    else:
        return COL.gray("# " + msg)


def print_debug(msg):
    print(_make_debug(msg))


def print_time_debug(msg):
    time = dt.now().strftime("%H:%M:%S.%f")
    msg = f"{time} {msg}"
    print_debug(msg)


def print_items(items):
    for item in items:
        print(item)


if __name__ == "__main__":
    show_all_colors()
