import os
import sys
from typing import Dict, List, Optional

import msgspec
import rich
import rich.status

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
    environment: Optional[str] = "default"
    shell: Optional[str] = None


class Environment(msgspec.Struct, forbid_unknown_fields=True):
    """
    name: The name of the environment.

    description: A description of the environment. Used in e.g. help messages.

    byggfiles: A list of Byggfiles that use this environment.

    inputs: A list of files that are used as input to the environment. Typically pip
    requirements files, but can be any files.

    venv_directory: The directory where the virtual environment is located. Will be
    recreated by Bygg if any of the inputs are modified.

    shell: The shell command for creating the environment.

    entrypoints: A list of entrypoints that use this environment.
    """

    byggfile: str
    inputs: List[str]
    venv_directory: str
    shell: str
    description: Optional[str] = None
    message: Optional[str] = None


class ByggFile(msgspec.Struct, forbid_unknown_fields=True):
    actions: List[ActionItem]
    settings: SettingsSection = msgspec.field(default_factory=SettingsSection)
    environments: Dict[str, Environment] = msgspec.field(default_factory=dict)


def load_python_build_file(build_file: str):
    if os.path.isfile(build_file):
        with open(build_file, "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 dump_schema():
    import json

    schema = msgspec.json.schema(ByggFile)
    print(json.dumps(schema, indent=2))
