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

import msgspec

from bygg.output import Symbols, output_plain
from bygg.output import TerminalStyle as TS

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


class Settings(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. If not set, this is treated as true
    by default in Byggfile.yml and false when used from Python.
    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: Settings = msgspec.field(default_factory=Settings)
    environments: Dict[str, Environment] = msgspec.field(default_factory=dict)


def read_config_file() -> ByggFile:
    if not os.path.isfile(YAML_INPUTFILE):
        return ByggFile(actions=[], settings=Settings(), environments={})

    try:
        with open(YAML_INPUTFILE, "r") as f:
            config_file = msgspec.yaml.decode(f.read(), type=ByggFile)
        for action in config_file.actions:
            if action.is_entrypoint is None:
                action.is_entrypoint = True
        return config_file
    except Exception as e:
        output_plain(
            Symbols.RED_X
            + TS.Fg.RED
            + " Error while reading configuration file "
            + TS.Fg.RESET
            + f"{TS.BOLD}{YAML_INPUTFILE}{TS.RESET}. {e}"
        )
        sys.exit(1)


def dump_schema():
    import json

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