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):

    """
    This is a representation of the Action class used for deserialising from YAML.

    Parameters
    ----------
    name : str
        The name of the action.
    description : str, optional
        A description of the action. Used in e.g. action listings. Default is None.
    message : str, optional
        A message to print when the action is executed. Default is None.
    inputs : list of str, optional
        A list of files that are used as input to the action. Default is None.
    outputs : list of str, optional
        A list of files that are generated by the action. Default is None.
    dependencies : list of str, optional
        A list of actions that must be executed before this action. Default is None.
    is_entrypoint : bool, optional
        Whether this action is an entrypoint. Entrypoints 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. Default is None.
    environment : str, optional
        The environment for the action. Default is "default".
    shell : str, optional
        The shell command to execute when the action is run. Default is None.
    """

    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):
    """
    A class used to represent a virtual environment that actions can be run in.

    Attributes
    ----------
    byggfile : str
        The Python Byggfile that uses this environment.
    inputs : List[str]
        A list of files that are used as input to the environment. Typically pip
        requirements files, but can be any files.
    venv_directory : str
        The directory where the virtual environment is located. Will be recreated by
        Bygg if any of the inputs are modified.
    shell : str
        The shell command for creating the environment.
    description : str, optional
        A description of the environment. Used in e.g. help messages, by default None
    message : str, optional
        A message related to the environment, by default None
    """

    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))
