"""Hub operators: account actions and managed tool install/remove."""

from __future__ import annotations

import queue
import tempfile
import threading
from pathlib import Path

import bpy

from . import auth, catalog, host, manager, panels
from ._core import api, config

JOB = {"addon_id": None, "message": ""}


def _redraw_views():
    for window in bpy.context.window_manager.windows:
        for area in window.screen.areas:
            if area.type == "VIEW_3D":
                area.tag_redraw()


def _prepare_install(entry, site, token, results):
    try:
        with tempfile.TemporaryDirectory(prefix="three-blocks-addon-") as directory:
            archive = Path(directory) / "addon.zip"
            api.download_artifact(site, entry["artifact"], token, archive)
            staged = manager.prepare_archive(entry, archive)
        results.put(("ready", str(staged)))
    except Exception as error:
        results.put(("error", str(error)))


class TB_OT_open_web(bpy.types.Operator):
    bl_idname = "three_blocks.open_web"
    bl_label = "Open threejs-blocks.com"
    bl_description = "Open the Three Blocks site in your browser"

    path: bpy.props.StringProperty(default="")

    def execute(self, context):
        bpy.ops.wm.url_open(url=host.site_url() + self.path)
        return {"FINISHED"}


class TB_OT_refresh_status(bpy.types.Operator):
    bl_idname = "three_blocks.refresh_status"
    bl_label = "Refresh"
    bl_description = (
        "Refresh account status and look for available tool and hub updates"
    )

    def execute(self, context):
        panels.invalidate_status()
        auth.refresh_verdict_async()
        catalog.check_async()
        return {"FINISHED"}


class TB_OT_sign_out(bpy.types.Operator):
    """Deletes the SHARED credential file entry — the terminal CLI signs out too."""

    bl_idname = "three_blocks.sign_out"
    bl_label = "Sign out of Three Blocks?"
    bl_description = (
        "Remove the local credential. It is shared with the three-blocks CLI, "
        "so this signs the CLI out as well"
    )

    def invoke(self, context, event):
        return context.window_manager.invoke_confirm(self, event)

    def execute(self, context):
        existed = config.clear_credential(host.site_url())
        auth.set_verdict(None)
        panels.invalidate_status()
        catalog.check_async()  # anonymous re-fetch → new install/update rows lock again
        if existed:
            self.report({"INFO"}, "Signed out (the three-blocks CLI too)")
        else:
            self.report({"INFO"}, "No local credential to remove")
        return {"FINISHED"}


class TB_OT_install_tool(bpy.types.Operator):
    bl_idname = "three_blocks.install_tool"
    bl_label = "Install Three Blocks tool"
    bl_description = "Download, verify, and install this managed tool add-on"

    addon_id: bpy.props.StringProperty()

    def invoke(self, context, event):
        if JOB["addon_id"]:
            self.report({"INFO"}, "Another Three Blocks tool is already installing")
            return {"CANCELLED"}
        entry = catalog.addon(self.addon_id)
        if not entry or not entry.get("available") or not entry.get("entitled"):
            self.report({"ERROR"}, "This tool is not available for this account")
            return {"CANCELLED"}
        credential = config.load_credential(host.site_url())
        if not credential:
            self.report({"ERROR"}, "Sign in before installing tools")
            return {"CANCELLED"}
        if not host.online_allowed():
            self.report({"ERROR"}, "Online access is disabled in Preferences ▸ System")
            return {"CANCELLED"}

        self._entry = dict(entry)
        self._results = queue.Queue()
        self._timer = context.window_manager.event_timer_add(0.25, window=context.window)
        context.window_manager.modal_handler_add(self)
        JOB.update(addon_id=self.addon_id, message=f"Downloading {entry.get('displayName', 'tool')}…")
        threading.Thread(
            target=_prepare_install,
            args=(self._entry, host.site_url(), credential["token"], self._results),
            daemon=True,
        ).start()
        _redraw_views()
        return {"RUNNING_MODAL"}

    def modal(self, context, event):
        if event.type != "TIMER":
            return {"PASS_THROUGH"}
        try:
            kind, payload = self._results.get_nowait()
        except queue.Empty:
            return {"RUNNING_MODAL"}
        if kind == "error":
            return self._finish(context, {"ERROR"}, payload, "CANCELLED")
        if not config.load_credential(host.site_url()) or (
            auth.VERDICT is not None and not auth.VERDICT.get("entitled")
        ):
            manager.discard_staged(payload)
            return self._finish(context, {"ERROR"}, "Tool install authorization changed", "CANCELLED")
        try:
            manager.activate(self._entry, payload)
        except Exception as error:
            return self._finish(context, {"ERROR"}, str(error), "CANCELLED")
        name = self._entry.get("displayName") or self.addon_id
        return self._finish(context, {"INFO"}, f"Installed {name}", "FINISHED")

    def _finish(self, context, level, message, result):
        context.window_manager.event_timer_remove(self._timer)
        JOB.update(addon_id=None, message="")
        _redraw_views()
        self.report(level, message)
        return {result}


class TB_OT_remove_tool(bpy.types.Operator):
    bl_idname = "three_blocks.remove_tool"
    bl_label = "Remove Three Blocks tool?"
    bl_description = "Unload and remove this managed tool add-on"

    addon_id: bpy.props.StringProperty()
    module: bpy.props.StringProperty()
    display_name: bpy.props.StringProperty()

    def invoke(self, context, event):
        return context.window_manager.invoke_confirm(self, event)

    def execute(self, context):
        entry = catalog.addon(self.addon_id)
        module = self.module or (entry or {}).get("module")
        if not module or not manager.installed(module):
            self.report({"INFO"}, "Tool is not installed")
            return {"CANCELLED"}
        try:
            manager.remove(module)
        except Exception as error:
            self.report({"ERROR"}, str(error))
            return {"CANCELLED"}
        _redraw_views()
        self.report(
            {"INFO"},
            f"Removed {self.display_name or (entry or {}).get('displayName') or self.addon_id}",
        )
        return {"FINISHED"}


classes = (
    TB_OT_open_web,
    TB_OT_refresh_status,
    TB_OT_sign_out,
    TB_OT_install_tool,
    TB_OT_remove_tool,
)


def register():
    for cls in classes:
        bpy.utils.register_class(cls)


def unregister():
    for cls in reversed(classes):
        bpy.utils.unregister_class(cls)
    JOB.update(addon_id=None, message="")
