"""
Auto-generated typed classes for ConfigMeta classes.

This module provides IDE-friendly typed interfaces for all configuration classes.
The reason we auto-generate this file is because we want to provide a bridge between what is the ConfigMeta classes and the typed programmatic interface.
The CoreConfig class is setup in a way that if any additionally params are missed out from being auto-generated then it will not affect the core functionality of the programmatic API.
The new parameters will just not show up in IDE autocompletions.
It is fine if this file is not regularly updated by running the script in the .pre-commit-config.app-changes.yaml
but it is recommended that this file not be deleted or manually edited.

"""

from typing import Optional, List, Dict, Any
from .unified_config import CoreConfig

import sys
from typing import TYPE_CHECKING

# on 3.8+ use the stdlib TypedDict;
# in TYPE_CHECKING blocks mypy/pyright still pick it up on older Pythons
if sys.version_info >= (3, 8):
    from typing import TypedDict
else:
    if TYPE_CHECKING:
        # for the benefit of type-checkers
        from typing import TypedDict  # noqa: F401
    # runtime no-op TypedDict shim
    class _TypedDictMeta(type):
        def __new__(cls, name, bases, namespace, total=True):
            # ignore total at runtime
            return super().__new__(cls, name, bases, namespace)

    class TypedDict(dict, metaclass=_TypedDictMeta):
        # Runtime stand-in for typing.TypedDict on <3.8.
        pass


class ResourceConfigDict(TypedDict, total=False):
    cpu: Optional[str]
    memory: Optional[str]
    gpu: Optional[str]
    disk: Optional[str]
    shared_memory: Optional[str]


class AuthConfigDict(TypedDict, total=False):
    type: Optional[str]
    public: Optional[bool]


class ReplicaConfigDict(TypedDict, total=False):
    fixed: Optional[int]
    min: Optional[int]
    max: Optional[int]
    scaling_policy: Optional["ScalingPolicyConfigDict"]


class ScalingPolicyConfigDict(TypedDict, total=False):
    rpm: Optional[int]


class DependencyConfigDict(TypedDict, total=False):
    from_requirements_file: Optional[str]
    from_pyproject_toml: Optional[str]
    python: Optional[str]
    pypi: Optional[dict]
    conda: Optional[dict]


class PackageConfigDict(TypedDict, total=False):
    src_paths: Optional[list]
    suffixes: Optional[list]


class TypedCoreConfig:
    """
    Parameters
    ----------
    name : str, optional
        The name of the app to deploy.

    port : int, optional
        Port where the app is hosted. When deployed this will be port on which we will deploy the app.

    description : str, optional
        The description of the app to deploy.

    app_type : str, optional
        The User defined type of app to deploy. Its only used for bookkeeping purposes.

    image : str, optional
        The Docker image to deploy with the App.

    tags : list, optional
        The tags of the app to deploy.

    secrets : list, optional
        Outerbounds integrations to attach to the app. You can use the value you set in the `@secrets` decorator in your code.

    compute_pools : list, optional
        A list of compute pools to deploy the app to.

    environment : dict, optional
        Environment variables to deploy with the App.

    commands : list, optional
        A list of commands to run the app with.

    resources : ResourceConfigDict, optional
        Resource configuration for the app.
            - cpu (str)
                CPU requests
            - memory (str)
                Memory requests
            - gpu (str)
                GPU requests
            - disk (str)
                Storage disk size.
            - shared_memory (str)
                Shared memory

    auth : AuthConfigDict, optional
        Auth related configurations.
            - type (str)
                The type of authentication to use for the app.
            - public (bool)
                Whether the app is public or not.

    replicas : ReplicaConfigDict, optional
        The number of replicas to deploy the app with.
            - fixed (int)
                The fixed number of replicas to deploy the app with. If min and max are set, this will raise an error.
            - min (int)
                The minimum number of replicas to deploy the app with.
            - max (int)
                The maximum number of replicas to deploy the app with.
            - scaling_policy (ScalingPolicyConfigDict)
                Scaling policy defines the the metric based on which the replicas will horizontally scale. If min and max replicas are set and are not the same, then a scaling policy will be applied. Default scaling policies can be 60 rpm (ie 1 rps).
                - rpm (int)
                    Scale up replicas when the requests per minute crosses this threshold. If nothing is provided and the replicas.max and replicas.min is set then the default rpm would be 60.

    code_package : tuple, optional
        Pre-packaged code from package_code(). A PackagedCode namedtuple containing url and key.

    force_upgrade : bool, optional
        Force upgrade the app even if it is currently being upgraded.

    persistence : str, optional
        The persistence mode to deploy the app with.
        [Experimental] May change in the future.

    project : str, optional
        The project name to deploy the app to.
        [Experimental] May change in the future.

    branch : str, optional
        The branch name to deploy the app to.
        [Experimental] May change in the future.

    models : list, optional
        [Experimental] May change in the future.

    data : list, optional
        [Experimental] May change in the future.

    generate_static_url : bool, optional
        Generate a static URL for the app based on its name.
    """

    def __init__(
        self,
        name: Optional[str] = None,
        port: Optional[int] = None,
        description: Optional[str] = None,
        app_type: Optional[str] = None,
        image: Optional[str] = None,
        tags: Optional[list] = None,
        secrets: Optional[list] = None,
        compute_pools: Optional[list] = None,
        environment: Optional[dict] = None,
        commands: Optional[list] = None,
        resources: Optional[ResourceConfigDict] = None,
        auth: Optional[AuthConfigDict] = None,
        replicas: Optional[ReplicaConfigDict] = None,
        code_package: Optional[tuple] = None,
        force_upgrade: Optional[bool] = None,
        persistence: Optional[str] = None,
        project: Optional[str] = None,
        branch: Optional[str] = None,
        models: Optional[list] = None,
        data: Optional[list] = None,
        generate_static_url: Optional[bool] = None,
        **kwargs
    ) -> None:
        self._kwargs = {
            "name": name,
            "port": port,
            "description": description,
            "app_type": app_type,
            "image": image,
            "tags": tags,
            "secrets": secrets,
            "compute_pools": compute_pools,
            "environment": environment,
            "commands": commands,
            "resources": resources,
            "auth": auth,
            "replicas": replicas,
            "code_package": code_package,
            "force_upgrade": force_upgrade,
            "persistence": persistence,
            "project": project,
            "branch": branch,
            "models": models,
            "data": data,
            "generate_static_url": generate_static_url,
        }
        # Add any additional kwargs
        self._kwargs.update(kwargs)
        # Remove None values
        self._kwargs = {k: v for k, v in self._kwargs.items() if v is not None}
        self._config_class = CoreConfig
        self._config = self.create_config()
        self._init()

    def create_config(self) -> CoreConfig:
        return CoreConfig.from_dict(self._kwargs)

    def to_dict(self) -> Dict[str, Any]:
        return self._config.to_dict()

    def _init(self):
        raise NotImplementedError
