"""The Three Blocks sidebar: account status and managed tool controls."""

from __future__ import annotations

import os

import bpy

from . import auth, brand, catalog, host, manager, ops
from ._core import api, config

# Static fallback rows until the first catalog fetch lands — same tools the
# server registry (blender-catalog.ts) lists today.
_FALLBACK_ADDONS = (
    {
        "id": "utsbv",
        "module": "utsubo_utsbv",
        "displayName": "Baked Motion",
        "description": "bake .utsbv motion",
        "available": False,
    },
    {"displayName": "Vertex Animated Video", "description": "bake mesh animation", "available": False},
    {"displayName": "Object Animation Video", "description": "bake rigid-object animation", "available": False},
    {"displayName": "Mesh to Splat", "description": "one-button still and animated Gaussian capture", "available": False},
)

# Sign-in status is re-read only when the shared credentials file changes —
# draw() runs on every viewport redraw, so it must stay cheap (one stat).
_status_cache: dict | None = None
_status_mtime: float | None = None
_status_site: str | None = None


def invalidate_status() -> None:
    global _status_cache, _status_mtime, _status_site
    _status_cache = None
    _status_mtime = None
    _status_site = None


def signin_status(site: str) -> dict:
    global _status_cache, _status_mtime, _status_site
    try:
        mtime = os.stat(config.credentials_path()).st_mtime
    except OSError:
        mtime = None
    if _status_cache is None or mtime != _status_mtime or site != _status_site:
        credential = config.load_credential(site)
        _status_cache = {
            "signed_in": credential is not None,
            "name": (credential or {}).get("name"),
            "token_prefix": (credential or {}).get("tokenPrefix"),
        }
        _status_mtime = mtime
        _status_site = site
    return _status_cache


class TB_PT_hub(bpy.types.Panel):
    bl_idname = "TB_PT_hub"
    bl_label = "Three Blocks"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"
    bl_category = "Three Blocks"

    def draw_header(self, _context):
        self.layout.label(text="", icon_value=brand.icon("logo"))

    def draw(self, context):
        layout = self.layout
        site = host.site_url()
        status = signin_status(site)

        card = layout.box()
        if status["signed_in"]:
            self._draw_signed_in(card, status)
        elif auth.STATE["phase"] == "waiting":
            card.label(text="Waiting for your browser…", icon="TIME")
            if auth.STATE["message"]:
                muted = card.column()
                muted.enabled = False
                muted.label(text=auth.STATE["message"])
        else:
            self._draw_signed_out(card)

        if catalog.update_available():
            note = layout.box()
            note.label(text=f"Hub update available — v{catalog.hub_latest()}", icon_value=brand.icon("block_tri"))
            note.label(text="Run `three-blocks install blender` to update.")

        self._draw_tools(layout, status)

        footer = layout.row(align=True)
        muted = footer.row()
        muted.enabled = False
        muted.label(text=f"hub v{catalog.installed_hub_version()}")
        footer.operator("three_blocks.open_web", text="", icon="URL")
        footer.operator("three_blocks.refresh_status", text="", icon="FILE_REFRESH")

    def _draw_signed_in(self, card, status):
        # The live verdict names the USER (account name > email); the stored
        # credential name is a machine label, the token prefix a last resort.
        verdict = auth.VERDICT
        who = api.display_name(verdict) or status["name"] or status["token_prefix"] or "your account"
        card.label(text=f"Signed in as {who}", icon="CHECKMARK")
        if verdict:
            plan = verdict.get("planDisplay") or verdict.get("plan") or "unknown plan"
            entitled = bool(verdict.get("entitled"))
            card.label(
                text=f"{plan} — {'Pro downloads available' if entitled else 'No Pro download access'}",
                icon="KEYFRAME_HLT" if entitled else "KEYFRAME",
            )
        card.operator("three_blocks.sign_out", text="Sign out", icon="X")

    def _draw_tools(self, layout, status):
        tools = layout.box()
        tools.label(text="Tools", icon_value=brand.icon("block_sq"))
        # Locks come from the catalog's per-addon entitled flags (fetched with
        # the credential). Before that lands, fall back to the validate verdict
        # so a fresh sign-in unlocks without waiting on the second fetch.
        entries = catalog.addons()
        verdict_entitled = bool((auth.VERDICT or {}).get("entitled"))
        if entries is None:
            entries = [{**addon, "entitled": verdict_entitled} for addon in _FALLBACK_ADDONS]
        any_installable = False
        for addon in entries:
            entitled = bool(addon.get("entitled"))
            available = bool(addon.get("available"))
            module = addon.get("module")
            installed = manager.installed_version(module)
            any_installable = any_installable or available or bool(installed)
            row = tools.row()
            title = addon.get("displayName") or addon.get("id") or "Tool"
            description = addon.get("description")
            label = f"{title} — {description}" if description else title
            can_update = bool(
                installed
                and entitled
                and available
                and catalog.newer_version(addon.get("version"), installed)
            )
            if installed and not can_update:
                row.enabled = not bool(ops.JOB["addon_id"])
                row.label(text=label, icon="CHECKMARK")
                remove = row.operator("three_blocks.remove_tool", text="", icon="TRASH")
                remove.addon_id = addon.get("id", "")
                remove.module = module or ""
                remove.display_name = title
            elif not entitled:
                row.enabled = False
                row.label(text=label, icon="LOCKED")
            elif not available:
                row.enabled = False
                row.label(text=f"{label} — Coming soon", icon="TIME")
            else:
                busy = ops.JOB["addon_id"] == addon.get("id")
                row.enabled = not bool(ops.JOB["addon_id"])
                row.label(text=label, icon="CHECKMARK" if installed else "UNLOCKED")
                install = row.operator(
                    "three_blocks.install_tool",
                    text="Installing…" if busy else "Update" if installed else "Install",
                    icon="IMPORT",
                )
                install.addon_id = addon.get("id", "")
                if installed:  # an update on offer must not hide Remove
                    remove = row.operator("three_blocks.remove_tool", text="", icon="TRASH")
                    remove.addon_id = addon.get("id", "")
                    remove.module = module or ""
                    remove.display_name = title
        note = tools.column()
        note.enabled = False
        if ops.JOB["message"]:
            note.label(text=ops.JOB["message"], icon="TIME")
        elif any_installable:
            note.label(text="Installed tools appear as panels in this tab.")
        elif status["signed_in"]:
            note.label(text="Managed Blender tools are coming soon.")
        else:
            note.label(text="Sign in to download Pro tools.")

    def _draw_signed_out(self, card):
        card.label(text="Not signed in", icon="USER")
        if auth.STATE["phase"] == "error" and auth.STATE["message"]:
            card.label(text=auth.STATE["message"], icon="ERROR")
        button = card.row()
        button.scale_y = 1.3
        button.operator("three_blocks.login", text="Sign in to Three Blocks", icon="UNLOCKED")
        muted = card.column()
        muted.enabled = False
        muted.label(text="Opens your browser · one seat per person")
        muted.label(text="(or run `three-blocks login` in a terminal)")


classes = (TB_PT_hub,)


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


def unregister():
    for cls in reversed(classes):
        bpy.utils.unregister_class(cls)
    brand.release()
