import json
import re
from pathlib import Path

import toml
import tomlkit
from flask import flash


# Get validation checks from template
def get_checks():
    template = toml.load("utils/.config.template.toml")
    checks = {}

    def unpack_checks(obj: dict):
        for key in obj.keys():
            if "optional" in obj[key].keys():
                checks[key] = obj[key]
            else:
                unpack_checks(obj[key])

    unpack_checks(template)

    return checks


# Get current config (from config.toml) as dict
def get_config(obj: dict, done={}):
    for key in obj.keys():
        if not isinstance(obj[key], dict):
            done[key] = obj[key]
        else:
            get_config(obj[key], done)

    return done


# Checks if value is valid
def check(value, checks):
    incorrect = False

    if value == "False":
        value = ""

    if not incorrect and "type" in checks:
        try:
            value = eval(checks["type"])(value)
        except Exception:
            incorrect = True

    if (
        not incorrect and "options" in checks and value not in checks["options"]
    ):  # FAILSTATE Value is not one of the options
        incorrect = True
    if (
        not incorrect
        and "regex" in checks
        and (
            (isinstance(value, str) and re.match(checks["regex"], value) is None)
            or not isinstance(value, str)
        )
    ):  # FAILSTATE Value doesn't match regex, or has regex but is not a string.
        incorrect = True

    if (
        not incorrect
        and not hasattr(value, "__iter__")
        and (
            ("nmin" in checks and checks["nmin"] is not None and value < checks["nmin"])
            or (
                "nmax" in checks
                and checks["nmax"] is not None
                and value > checks["nmax"]
            )
        )
    ):
        incorrect = True

    if (
        not incorrect
        and hasattr(value, "__iter__")
        and (
            (
                "nmin" in checks
                and checks["nmin"] is not None
                and len(value) < checks["nmin"]
            )
            or (
                "nmax" in checks
                and checks["nmax"] is not None
                and len(value) > checks["nmax"]
            )
        )
    ):
        incorrect = True

    if incorrect:
        return "Error"

    return value


# Modify settings (after form is submitted)
def modify_settings(data: dict, config_load, checks: dict):
    # Modify config settings
    def modify_config(obj: dict, name: str, value: any):
        for key in obj.keys():
            if name == key:
                obj[key] = value
            elif not isinstance(obj[key], dict):
                continue
            else:
                modify_config(obj[key], name, value)

    # Remove empty/incorrect key-value pairs
    data = {key: value for key, value in data.items() if value and key in checks.keys()}

    # Validate values
    for name in data.keys():
        value = check(data[name], checks[name])

        # Value is invalid
        if value == "Error":
            flash("Some values were incorrect and didn't save!", "error")
        else:
            # Value is valid
            modify_config(config_load, name, value)

    # Save changes in config.toml
    with Path("config.toml").open("w") as toml_file:
        toml_file.write(tomlkit.dumps(config_load))

    flash("Settings saved!")

    return get_config(config_load)


# Delete background video
def delete_background(key):
    # Read backgrounds.json
    with open("utils/backgrounds.json", "r", encoding="utf-8") as backgrounds:
        data = json.load(backgrounds)

    # Remove background from backgrounds.json
    with open("utils/backgrounds.json", "w", encoding="utf-8") as backgrounds:
        if data.pop(key, None):
            json.dump(data, backgrounds, ensure_ascii=False, indent=4)
        else:
            flash("Couldn't find this background. Try refreshing the page.", "error")
            return

    # Remove background video from ".config.template.toml"
    config = tomlkit.loads(Path("utils/.config.template.toml").read_text())
    config["settings"]["background"]["background_choice"]["options"].remove(key)

    with Path("utils/.config.template.toml").open("w") as toml_file:
        toml_file.write(tomlkit.dumps(config))

    flash(f'Successfully removed "{key}" background!')


# Add background video
def add_background(youtube_uri, filename, citation, position):
    # Validate YouTube URI
    regex = re.compile(r"(?:\/|%3D|v=|vi=)([0-9A-z-_]{11})(?:[%#?&]|$)").search(
        youtube_uri
    )

    if not regex:
        flash("YouTube URI is invalid!", "error")
        return

    youtube_uri = f"https://www.youtube.com/watch?v={regex.group(1)}"

    # Check if position is valid
    if position == "" or position == "center":
        position = "center"

    elif position.isdecimal():
        position = int(position)

    else:
        flash('Position is invalid! It can be "center" or decimal number.', "error")
        return

    # Sanitize filename
    regex = re.compile(r"^([a-zA-Z0-9\s_-]{1,100})$").match(filename)

    if not regex:
        flash("Filename is invalid!", "error")
        return

    filename = filename.replace(" ", "_")

    # Check if background doesn't already exist
    with open("utils/backgrounds.json", "r", encoding="utf-8") as backgrounds:
        data = json.load(backgrounds)

        # Check if key isn't already taken
        if filename in list(data.keys()):
            flash("Background video with this name already exist!", "error")
            return

        # Check if the YouTube URI isn't already used under different name
        if youtube_uri in [data[i][0] for i in list(data.keys())]:
            flash("Background video with this YouTube URI is already added!", "error")
            return

    # Add background video to json file
    with open("utils/backgrounds.json", "r+", encoding="utf-8") as backgrounds:
        data = json.load(backgrounds)

        data[filename] = [youtube_uri, filename + ".mp4", citation, position]
        backgrounds.seek(0)
        json.dump(data, backgrounds, ensure_ascii=False, indent=4)

    # Add background video to ".config.template.toml"
    config = tomlkit.loads(Path("utils/.config.template.toml").read_text())
    config["settings"]["background"]["background_choice"]["options"].append(filename)

    with Path("utils/.config.template.toml").open("w") as toml_file:
        toml_file.write(tomlkit.dumps(config))

    flash(f'Added "{citation}-{filename}.mp4" as a new background video!')

    return