import os

from m.core import Bad, Good, OneOf
from pydantic import BaseModel


class MissingEnvVars(BaseModel):
    """Container for missing environment variables."""

    # list of environment variables that are required but do not exist
    missing: list[str]

    # environment variables that are defined
    defined: dict[str, str]


def require_env_vars(env_vars: list[str]) -> OneOf[MissingEnvVars, None]:
    """Require that the given environment variables are defined.

    Args:
        env_vars: list of environment variables to check.

    Returns:
        None if all environment variables are defined, otherwise a
        MissingEnvVars instance.
    """
    missing: list[str] = []
    defined: dict[str, str] = {}
    for env_var in env_vars:
        env_var_value = os.environ.get(env_var)
        if env_var_value is None:
            missing.append(env_var)
        else:
            defined[env_var] = env_var_value
    if missing:
        return Bad(MissingEnvVars(missing=missing, defined=defined))
    return Good(None)


class DevcontainerEnvVars(BaseModel):
    """Collection of variables required for devcontainer operations.

    All properties are prefixed with `MDC` to denote that those are variables
    generated by `M Dev Container`.
    """

    mdc_repo: str
    mdc_workspace: str
    mdc_pnpm_workspace: str
    mdc_venv_workspace: str

    def to_bash(self: 'DevcontainerEnvVars') -> str:
        """Create a bash code snippet that can be used in a bashrc file.

        Returns:
            A `.bashrc` code snippet.
        """
        dict_data = self.model_dump()
        return '\n'.join([
            f"export {var_name.upper()}='{var_value}'"
            for var_name, var_value in dict_data.items()
        ])


def devcontainer_env_vars() -> DevcontainerEnvVars:
    """Get the environment variables for a devcontainer.

    This function is intended to be used in a devcontainer or in a Github
    runner. It will return the following values::

        - repo: The repository name.
        - workspace: The path to directory containing the repo files.
        - pnpmWorkspace: The path where pnpm operations should take place.
        - venvWorkspace: The path where venv operations should take place.

    Returns:
        A dictionary with the values described above.
    """
    github = os.environ.get('GITHUB_WORKSPACE')
    container = os.environ.get('CONTAINER_WORKSPACE')
    workspace = github or container or 'ERROR_UNKNOWN_WORKSPACE'
    # big assumption here that the name of the repo is reflected in the name
    # of the containing directory. Trying to avoid having to call `git`.
    repo_name = workspace.split('/')[-1]
    pnpm_workspace = f'/opt/pnpm/{repo_name}'
    venv_workspace = f'/opt/venv/{repo_name}'
    return DevcontainerEnvVars(
        mdc_repo=repo_name,
        mdc_workspace=workspace,
        mdc_pnpm_workspace=pnpm_workspace,
        mdc_venv_workspace=venv_workspace,
    )
