"""
Provides functionality to install packages from plain Docker images
using Malevich Core capabilities
"""

import hashlib
import re
from typing import Optional

from malevich._utility import Stub
from malevich.constants import DEFAULT_CORE_HOST, IMAGE_BASE
from malevich.install import Installer
from malevich.manifest import ManifestManager
from malevich.models import ImageDependency, ImageOptions
from malevich.path import Paths

from .._core.scan import scan_core

_pydantic_types = {
    "string": str.__name__,
    "integer": int.__name__,
    "number": float.__name__,
    "boolean": bool.__name__,
}


class Templates:
    """Templates for creating a package from a Docker image"""

    disclaimer = """\"\"\"
THIS FILE IS AUTOGENERATED BY malevich PACKAGE INSTALLER.

THIS FILE CONTAINS VITAL INFORMATION ABOUT THE PACKAGE AND ITS CONTENTS.
DO NOT MODIFY THIS FILE MANUALLY.
\"\"\"
"""

    imports = """
from malevich._autoflow.function import autotrace, sinktrace
from malevich._utility import Registry
from malevich.models.nodes import OperationNode

from pydantic import BaseModel
"""
    registry = """
Registry().register("{operation_id}", {{
    "image_ref": {image_ref},
    "image_auth_user": {image_auth_user},
    "image_auth_pass": {image_auth_pass},
    "processor_id": "{processor_id}",
}})
"""

    schema = """
class {schema_name}(BaseModel):
{schema_def}
"""

    processor = """
@{decor}
def {name}({args}config: dict = {{}}):
    \"\"\"{docs}\"\"\"
    return OperationNode(
        operation_id="{operation_id}",
        config=config,
        processor_id="{name}",
        package_id="{package_id}",
        alias=alias,
    )
"""
    init = """
# {init_name} = ...

"""

    input = """
# {input_name} = ...

"""

    output = """
# {output_name} = ...

"""


class ImageInstaller(Installer):
    """Installs package using Docker image"""
    name = "image"


    def install(
        self,
        package_name: str,
        image_ref: str,
        image_auth: tuple[str, str],
        core_host: str = DEFAULT_CORE_HOST,
        core_auth: Optional[tuple[str, str]] = None,
    ) -> ImageDependency:
        package_name = re.sub(r'[\W\s]+', '_', package_name)
        app_info = scan_core(
            core_auth=core_auth,
            core_host=core_host,
            image_ref=image_ref,
            image_auth=image_auth,
        )
        checksum = hashlib.sha256(
            app_info.model_dump_json().encode()).hexdigest()
        m = ManifestManager()
        iauth_user, iauth_pass, cauth_user, cauth_token = m.put_secrets(
            image_auth_user=image_auth[0],
            image_auth_password=image_auth[1],
            core_auth_user=core_auth[0] if core_auth else None,
            core_auth_token=core_auth[1] if core_auth else None,
            salt=checksum,
        )
        dependency = ImageDependency(
            package_id=package_name,
            version="",
            installer="image",
            options=ImageOptions(
                core_host=core_host,
                core_auth_token=cauth_token,
                core_auth_user=cauth_user,
                image_auth_user=iauth_user,
                image_auth_pass=iauth_pass,
                image_ref=image_ref,
            ),
        )

        operation_ids = {
            str(op.id): hashlib.sha256(op.model_dump_json().encode()).hexdigest()
            for op in [*app_info.processors.values(), *app_info.conditions.values()]
        }

        operation_names = {
            str(op.id): op.name
            for op in [*app_info.processors.values(), *app_info.conditions.values()]
        }

        Stub.from_app_info(
            app_info=app_info,
            path=Paths.module(package_name),
            package_name=package_name,
            dependency=dependency,
            operation_ids=operation_ids,
            registry_records={
                processor_id: {
                    "operation_id": operation_id,
                    "image_ref":  ('dependencies', package_name, 'options', 'image_ref'),  # noqa: E501
                    "image_auth_user": ('dependencies', package_name, 'options', 'image_auth_user'),  # noqa: E501
                    "image_auth_pass": ('dependencies', package_name, 'options', 'image_auth_pass'),  # noqa: E501
                    "processor_id": operation_names[processor_id],
                }
                for processor_id, operation_id in operation_ids.items()
            },
        )

        return dependency

    def restore(self, dependency: ImageDependency) -> ImageDependency:
        core_user = dependency.options.core_auth_user
        core_token = dependency.options.core_auth_token
        image_ref = dependency.options.image_ref
        if not image_ref:
            image_ref = IMAGE_BASE.format(app=dependency.package_id)

        return self.install(
            package_name=dependency.package_id,
            image_ref=image_ref,
            image_auth=(
                dependency.options.image_auth_user or "",
                dependency.options.image_auth_pass or ""
            ),
            core_host=dependency.options.core_host,
            core_auth=(
                core_user,
                core_token,
            ) if core_user and core_token else None,
        )

    def construct_dependency(self, object: dict) -> ImageDependency:
        manf = ManifestManager()
        parsed = ImageDependency(**object)
        parsed.options.image_auth_pass = manf.query_secret(
            parsed.options.image_auth_pass,
            only_value=True,
        )
        parsed.options.image_auth_user = manf.query_secret(
            parsed.options.image_auth_user,
            only_value=True,
        )
        parsed.options.core_auth_user = manf.query_secret(
            parsed.options.core_auth_user,
            only_value=True,
        )
        parsed.options.core_auth_token = manf.query_secret(
            parsed.options.core_auth_token,
            only_value=True,
        )
        return parsed
