from pathlib import Path
import time

import bpy
from bpy_extras.io_utils import ImportHelper
from bpy.props import BoolProperty, StringProperty

from .auth_client import (
    AuthorizationError,
    report_import_event,
    require_import_entitlement,
    resolve_protected_package_key,
)
from .package_reader import PackageError, SceneIRPackage
from .protected_package import is_protected_package_path
from .scene_importer import SceneIRImporter


class IMPORT_SCENE_OT_threedripper_helper_ir(bpy.types.Operator, ImportHelper):
    bl_idname = "import_scene.threedripper_helper_ir"
    bl_label = "Import 3DRipper Scene IR"
    bl_options = {"UNDO"}

    filename_ext = ""
    filter_glob: StringProperty(
        default="*.3dripper;*.zip;blender-package.json",
        options={"HIDDEN"},
    )
    pack_images: BoolProperty(
        name="Pack Images",
        description="Pack imported texture bytes into the .blend file",
        default=True,
    )

    def execute(self, context):
        started_at = time.monotonic()
        package_type = _package_type(self.filepath)
        try:
            require_import_entitlement(context)
        except AuthorizationError as error:
            report_import_event(
                package_type,
                "DENIED",
                context=context,
                error_code="ENTITLEMENT_DENIED",
                duration_ms=_duration_ms(started_at),
            )
            self.report({"ERROR"}, str(error))
            return {"CANCELLED"}

        # Unpacked images must survive the importer's temp directory, which is
        # deleted when the package closes; extract them next to the package.
        persist_dir = None
        if not self.pack_images:
            source = Path(self.filepath)
            persist_dir = source.with_name("{}_resources".format(source.stem))
        package_key_id = None
        try:
            with SceneIRPackage(
                self.filepath,
                persist_dir=persist_dir,
                key_resolver=lambda header: resolve_protected_package_key(
                    header["keyId"], context
                ),
            ) as package:
                result = SceneIRImporter(context, package, pack_images=self.pack_images).import_scene()
                warnings = list(package.scene.get("warnings", []))
                warnings.extend(package.manifest.get("warnings", []))
                if package.protected_header:
                    package_key_id = package.protected_header.get("keyId")
            unique_warnings = list(dict.fromkeys(str(item) for item in warnings))
            for warning in unique_warnings:
                self.report({"WARNING"}, warning)
            if persist_dir is not None:
                self.report({"INFO"}, "Textures extracted to {}".format(persist_dir))
            self.report({"INFO"}, "Imported {} objects, {} materials".format(
                result["objects"], result["materials"]
            ))
            report_import_event(
                package_type,
                "SUCCESS",
                context=context,
                package_key_id=package_key_id,
                object_count=result["objects"],
                material_count=result["materials"],
                warning_count=len(unique_warnings),
                duration_ms=_duration_ms(started_at),
            )
            return {"FINISHED"}
        except Exception as error:
            report_import_event(
                package_type,
                "FAILED",
                context=context,
                package_key_id=package_key_id,
                error_code=_error_code(error),
                duration_ms=_duration_ms(started_at),
            )
            self.report({"ERROR"}, str(error))
            return {"CANCELLED"}


def menu_func_import(self, _context):
    self.layout.operator(
        IMPORT_SCENE_OT_threedripper_helper_ir.bl_idname,
        text="3DRipper Scene IR Package (.3dripper/.zip)",
    )


def _package_type(filepath):
    source = Path(filepath)
    if source.is_dir() or source.name == "blender-package.json":
        return "DIRECTORY"
    if is_protected_package_path(source):
        return "PROTECTED"
    return "ZIP"


def _duration_ms(started_at):
    return max(0, min(86_400_000, int((time.monotonic() - started_at) * 1000)))


def _error_code(error):
    if isinstance(error, AuthorizationError):
        return "AUTHORIZATION_ERROR"
    if isinstance(error, PackageError):
        return "PACKAGE_ERROR"
    if isinstance(error, OSError):
        return "FILESYSTEM_ERROR"
    if isinstance(error, (ValueError, KeyError)):
        return "INVALID_SCENE_IR"
    return "IMPORT_ERROR"
