import time
import webbrowser

import bpy

from .auth_client import (
    AuthorizationError,
    AuthorizationPending,
    AuthorizationSlowDown,
    logout,
    poll_device_token,
    refresh_entitlement,
    request_device_authorization,
)


class WM_OT_threedripper_helper_login(bpy.types.Operator):
    bl_idname = "wm.threedripper_helper_login"
    bl_label = "Sign In to 3DRipper"
    bl_description = "Open 3DRipper in your browser and approve this Blender installation"

    _timer = None
    _device_code = None
    _expires_at = 0.0
    _next_poll_at = 0.0
    _poll_interval = 5.0

    def execute(self, context):
        try:
            device = request_device_authorization(context)
        except AuthorizationError as error:
            self.report({"ERROR"}, str(error))
            return {"CANCELLED"}

        self._device_code = device.device_code
        self._expires_at = time.monotonic() + device.expires_in
        self._poll_interval = float(device.interval)
        self._next_poll_at = time.monotonic() + self._poll_interval
        self._timer = context.window_manager.event_timer_add(1.0, window=context.window)
        context.window_manager.modal_handler_add(self)
        webbrowser.open(device.verification_uri_complete)
        self.report(
            {"INFO"},
            "Browser opened. Approve the request using code {}.".format(device.user_code),
        )
        return {"RUNNING_MODAL"}

    def modal(self, context, event):
        if event.type == "ESC":
            self._finish(context)
            self.report({"INFO"}, "3DRipper sign-in cancelled.")
            return {"CANCELLED"}
        if event.type != "TIMER":
            return {"PASS_THROUGH"}
        if time.monotonic() >= self._expires_at:
            self._finish(context)
            self.report({"ERROR"}, "The 3DRipper sign-in code expired. Start again.")
            return {"CANCELLED"}
        if time.monotonic() < self._next_poll_at:
            return {"PASS_THROUGH"}

        self._next_poll_at = time.monotonic() + self._poll_interval
        try:
            poll_device_token(self._device_code, context)
            entitlement = refresh_entitlement(context, force=True)
        except AuthorizationPending:
            return {"PASS_THROUGH"}
        except AuthorizationSlowDown:
            self._poll_interval += 5.0
            self._next_poll_at = time.monotonic() + self._poll_interval
            return {"PASS_THROUGH"}
        except AuthorizationError as error:
            self._finish(context)
            self.report({"ERROR"}, str(error))
            return {"CANCELLED"}

        self._finish(context)
        if entitlement.allowed:
            self.report({"INFO"}, "3DRipper account access verified.")
            return {"FINISHED"}
        self.report({"WARNING"}, entitlement.message)
        return {"FINISHED"}

    def _finish(self, context):
        if self._timer is not None:
            context.window_manager.event_timer_remove(self._timer)
            self._timer = None


class WM_OT_threedripper_helper_refresh_access(bpy.types.Operator):
    bl_idname = "wm.threedripper_helper_refresh_access"
    bl_label = "Refresh 3DRipper Access"
    bl_description = "Ask 3DRipper for your current Blender Add-on entitlement"

    def execute(self, context):
        try:
            entitlement = refresh_entitlement(context, force=True)
        except AuthorizationError as error:
            self.report({"ERROR"}, str(error))
            return {"CANCELLED"}
        if entitlement.allowed:
            self.report({"INFO"}, "3DRipper account access verified.")
        else:
            self.report({"WARNING"}, entitlement.message)
        return {"FINISHED"}


class WM_OT_threedripper_helper_logout(bpy.types.Operator):
    bl_idname = "wm.threedripper_helper_logout"
    bl_label = "Sign Out of 3DRipper"
    bl_description = "Remove this Blender installation's saved 3DRipper login"

    def execute(self, context):
        try:
            logout(context)
        except AuthorizationError as error:
            self.report({"ERROR"}, str(error))
            return {"CANCELLED"}
        self.report({"INFO"}, "Signed out of 3DRipper.")
        return {"FINISHED"}
