import os
import sys
from typing import List, Optional

import msgspec
import rich
import rich.status

from bygg.action import Action
from bygg.util import create_shell_command

PYTHON_INPUTFILE = "Byggfile.py"
YAML_INPUTFILE = "Byggfile.yml"


class SettingsSection(msgspec.Struct, forbid_unknown_fields=True):
    default_action: Optional[str] = None


class ActionItem(msgspec.Struct, forbid_unknown_fields=True):
    """
    name: The name of the action.
    description: A description of the action. Used in e.g. help messages.
    message: A message to print when the action is executed.
    inputs: A list of files that are used as input to the action.
    outputs: A list of files that are generated by the action.
    dependencies: A list of actions that must be executed before this action.
    is_entrypoint: Whether this action is an entrypoint. Entry points are actions that
    can be executed directly from the command line.
    shell: The shell command to execute when the action is run.
    """

    name: str
    description: Optional[str] = None
    message: Optional[str] = None
    inputs: Optional[List[str]] = None
    outputs: Optional[List[str]] = None
    dependencies: Optional[List[str]] = None
    is_entrypoint: Optional[bool] = None
    shell: Optional[str] = None


class ByggFile(msgspec.Struct):
    actions: List[ActionItem]
    settings: SettingsSection = msgspec.field(default_factory=SettingsSection)


def load_python_build_file():
    if os.path.isfile(PYTHON_INPUTFILE):
        with open(PYTHON_INPUTFILE, "r") as f:
            # modify load path to make the current directory importable
            preamble = "import sys\nsys.path.insert(0, '.')\n\n"
            exec(preamble + f.read(), globals())


def read_config_file() -> ByggFile | None:
    if not os.path.isfile(YAML_INPUTFILE):
        return None

    try:
        with open(YAML_INPUTFILE, "r") as f:
            return msgspec.yaml.decode(f.read(), type=ByggFile)
    except Exception as e:
        rich.print(
            "[red bold]Error while reading configuration file "
            f"[yellow]{YAML_INPUTFILE}[/yellow]:[/red bold] {e}"
        )
        sys.exit(1)


def apply_configuration(configuration: ByggFile | None):
    if not configuration:
        return

    for action in configuration.actions:
        shell_command = (
            create_shell_command(action.shell, action.message) if action.shell else None
        )
        Action(
            action.name,
            is_entrypoint=bool(action.is_entrypoint),
            inputs=action.inputs,
            outputs=action.outputs,
            dependencies=action.dependencies,
            command=shell_command,
        )
