from pathlib import Path
from typing import Optional, Dict, Any, List, Union
from typing_extensions import Literal

from phidata.app.db import DbApp
from phidata.infra.k8s.create.core.v1.service import ServiceType
from phidata.infra.k8s.enums.image_pull_policy import ImagePullPolicy
from phidata.infra.k8s.enums.restart_policy import RestartPolicy
from phidata.app.superset.superset_base import SupersetBase
from phidata.utils.log import logger


class SupersetWebserver(SupersetBase):
    def __init__(
        self,
        name: str = "superset-ws",
        version: str = "1",
        enabled: bool = True,
        # Image args,
        image_name: str = "apache/superset",
        image_tag: str = "latest",
        entrypoint: Optional[Union[str, List]] = None,
        command: Optional[Union[str, List]] = None,
        # Mount the workspace directory on the container,
        mount_workspace: bool = False,
        workspace_volume_name: Optional[str] = None,
        # Path to mount the workspace volume under,
        # This is the parent directory for the workspace on the container,
        # i.e. the ws is mounted as a subdir in this dir,
        # eg: if ws name is: idata, workspace_dir would be: /usr/local/idata,
        workspace_parent_container_path: str = "/usr/local",
        # NOTE: On DockerContainers the workspace_root_path is mounted to workspace_dir,
        # because we assume that DockerContainers are running locally on the user's machine,
        # On K8sContainers, we load the workspace_dir from git using a git-sync sidecar container,
        create_git_sync_sidecar: bool = True,
        git_sync_repo: Optional[str] = None,
        git_sync_branch: Optional[str] = None,
        git_sync_wait: int = 1,
        # But when running k8s locally, we can mount the workspace using,
        # host path as well.,
        k8s_mount_local_workspace: bool = False,
        # Superset resources directory relative to the workspace_root,
        # This directory contains all the files required by superset.,
        # eg: docker-bootstrap.sh,
        # This dir is mounted to the `/app/docker` directory on the container,
        mount_resources: bool = True,
        resources_volume_name: Optional[str] = None,
        resources_dir: str = "superset",
        resources_dir_container_path: str = "/app/docker",
        # Set the SUPERSET_CONFIG_PATH env var,
        superset_config_path: Optional[str] = None,
        # Set the REQUIREMENTS_LOCAL env var,
        # defaults to "/app/docker/requirements-local.txt",
        requirements_local: Optional[str] = None,
        # Set the PYTHONPATH env var,
        # defaults to "/app/pythonpath",
        python_path: Optional[str] = None,
        # Configure Superset database,
        # Get database details using DbApp,
        db_app: Optional[DbApp] = None,
        # Provide database details,
        # Set the DATABASE_USER env var,
        db_user: Optional[str] = None,
        # Set the DATABASE_PASSWORD env var,
        db_password: Optional[str] = None,
        # Set the DATABASE_DB env var,
        db_schema: Optional[str] = None,
        # Set the DATABASE_HOST env var,
        db_host: Optional[str] = None,
        # Set the DATABASE_PORT env var,
        db_port: Optional[int] = None,
        # Set the DATABASE_DIALECT env var,
        db_dialect: str = "postgresql+psycopg2",
        # Superset db connections in the format { conn_id: conn_url },
        db_connections: Optional[Dict] = None,
        # Configure superset redis,
        # Get redis details using PhidataApp,
        redis_app: Optional[Any] = None,
        # Provide redis details,
        # Set the REDIS_HOST env var,
        redis_host: str = "redis",
        # Set the REDIS_PORT env var,
        redis_port: int = 6379,
        # Set the FLASK_ENV env var,
        flask_env: str = "production",
        # Set the SUPERSET_ENV env var,
        superset_env: str = "production",
        # Set the SUPERSET_LOAD_EXAMPLES env var,
        superset_load_examples: str = "yes",
        # Configure the container,
        container_name: Optional[str] = None,
        image_pull_policy: ImagePullPolicy = ImagePullPolicy.IF_NOT_PRESENT,
        container_detach: bool = True,
        container_auto_remove: bool = True,
        container_remove: bool = True,
        # Add container labels,
        container_labels: Optional[Dict[str, Any]] = None,
        # NOTE: Available only for Docker,
        # Add volumes to DockerContainer,
        # container_volumes is a dictionary which adds the volumes to mount,
        # inside the container. The key is either the host path or a volume name,,
        # and the value is a dictionary with 2 keys:,
        #   bind - The path to mount the volume inside the container,
        #   mode - Either rw to mount the volume read/write, or ro to mount it read-only.,
        # For example:,
        # {,
        #   '/home/user1/': {'bind': '/mnt/vol2', 'mode': 'rw'},,
        #   '/var/www': {'bind': '/mnt/vol1', 'mode': 'ro'},
        # },
        container_volumes: Optional[Dict[str, dict]] = None,
        # Open a container port if open_container_port=True,
        open_container_port: bool = False,
        # Port number on the container,
        container_port: int = 8000,
        # Port name: Only used by the K8sContainer,
        container_port_name: str = "http",
        # Host port: Only used by the DockerContainer,
        container_host_port: int = 8000,
        # Open the app port if open_app_port=True,
        open_app_port: bool = False,
        # App port number on the container,
        app_port: int = 8088,
        # Only used by the K8sContainer,
        app_port_name: str = "app",
        # Only used by the DockerContainer,
        app_host_port: int = 8088,
        # Add env variables to container env,
        env: Optional[Dict[str, str]] = None,
        # Read env variables from a file in yaml format,
        env_file: Optional[Path] = None,
        # Configure the ConfigMap used for env variables that are not Secret,
        config_map_name: Optional[str] = None,
        # Configure the Secret used for env variables that are Secret,
        secret_name: Optional[str] = None,
        # Read secrets from a file in yaml format,
        secrets_file: Optional[Path] = None,
        # Configure the deployment,
        deploy_name: Optional[str] = None,
        pod_name: Optional[str] = None,
        replicas: int = 1,
        pod_node_selector: Optional[Dict[str, str]] = None,
        restart_policy: RestartPolicy = RestartPolicy.ALWAYS,
        termination_grace_period_seconds: Optional[int] = None,
        # Add deployment labels,
        deploy_labels: Optional[Dict[str, Any]] = None,
        # Determine how to spread the deployment across a topology,
        # Key to spread the pods across,
        topology_spread_key: Optional[str] = None,
        # The degree to which pods may be unevenly distributed,
        topology_spread_max_skew: Optional[int] = None,
        # How to deal with a pod if it doesn't satisfy the spread constraint.,
        topology_spread_when_unsatisfiable: Optional[
            Literal["DoNotSchedule", "ScheduleAnyway"],
        ] = None,
        # Configure the app service,
        create_app_service: bool = False,
        app_service_name: Optional[str] = None,
        app_service_type: Optional[ServiceType] = None,
        # The port that will be exposed by the service.,
        app_service_port: int = 8088,
        # The node_port that will be exposed by the service if app_service_type = ServiceType.NODE_PORT,
        app_node_port: Optional[int] = None,
        # The app_target_port is the port to access on the pods targeted by the service.,
        # It can be the port number or port name on the pod.,
        app_target_port: Optional[Union[str, int]] = None,
        # Add labels to app service,
        app_service_labels: Optional[Dict[str, Any]] = None,
        # Additional args
        # If True, use cached resources
        # i.e. skip resource creation/deletion if active resources with the same name exist.
        use_cache: bool = True,
    ):
        super().__init__(
            name=name,
            version=version,
            enabled=enabled,
            image_name=image_name,
            image_tag=image_tag,
            entrypoint=entrypoint,
            command=command,
            mount_workspace=mount_workspace,
            workspace_volume_name=workspace_volume_name,
            workspace_parent_container_path=workspace_parent_container_path,
            create_git_sync_sidecar=create_git_sync_sidecar,
            git_sync_repo=git_sync_repo,
            git_sync_branch=git_sync_branch,
            git_sync_wait=git_sync_wait,
            k8s_mount_local_workspace=k8s_mount_local_workspace,
            mount_resources=mount_resources,
            resources_volume_name=resources_volume_name,
            resources_dir=resources_dir,
            resources_dir_container_path=resources_dir_container_path,
            superset_config_path=superset_config_path,
            requirements_local=requirements_local,
            python_path=python_path,
            db_app=db_app,
            db_user=db_user,
            db_password=db_password,
            db_schema=db_schema,
            db_host=db_host,
            db_port=db_port,
            db_dialect=db_dialect,
            db_connections=db_connections,
            redis_app=redis_app,
            redis_host=redis_host,
            redis_port=redis_port,
            flask_env=flask_env,
            superset_env=superset_env,
            superset_load_examples=superset_load_examples,
            container_name=container_name,
            image_pull_policy=image_pull_policy,
            container_detach=container_detach,
            container_auto_remove=container_auto_remove,
            container_remove=container_remove,
            container_labels=container_labels,
            container_volumes=container_volumes,
            open_container_port=open_container_port,
            container_port=container_port,
            container_port_name=container_port_name,
            container_host_port=container_host_port,
            open_app_port=open_app_port,
            app_port=app_port,
            app_port_name=app_port_name,
            app_host_port=app_host_port,
            env=env,
            env_file=env_file,
            config_map_name=config_map_name,
            secret_name=secret_name,
            secrets_file=secrets_file,
            deploy_name=deploy_name,
            pod_name=pod_name,
            replicas=replicas,
            pod_node_selector=pod_node_selector,
            restart_policy=restart_policy,
            termination_grace_period_seconds=termination_grace_period_seconds,
            deploy_labels=deploy_labels,
            topology_spread_key=topology_spread_key,
            topology_spread_max_skew=topology_spread_max_skew,
            topology_spread_when_unsatisfiable=topology_spread_when_unsatisfiable,
            create_app_service=create_app_service,
            app_service_name=app_service_name,
            app_service_type=app_service_type,
            app_service_port=app_service_port,
            app_node_port=app_node_port,
            app_target_port=app_target_port,
            app_service_labels=app_service_labels,
            use_cache=use_cache,
        )
