import sublime
import sublime_plugin

import logging

import codemp
from ..core.session import session
from ..core.workspace import workspaces

from ..utils import get_setting
from ..quickpanel.qpbrowser import qpi
from ..quickpanel.qpbrowser import show_qp

from ..input_handlers import SimpleTextInput
from ..input_handlers import SimpleListInput

logger = logging.getLogger(__name__)


def valid_profiles(profiles):
    return [p for p in profiles if "name" in p]


def get_profile_index_by_name(profiles, name):
    return next((index for (index, d) in enumerate(profiles) if d["name"] == name))


def get_default_profile(profiles):
    i = get_profile_index_by_name(profiles, "Default")
    return profiles[i]


class CodempConnectCommand(sublime_plugin.WindowCommand):
    def __init__(self, window):
        super().__init__(window)
        self.connprofile = {}

    def is_enabled(self) -> bool:
        return not session.is_active()

    def update_config_and_connect(self):
        config = codemp.Config(
            username = self.connprofile["username"],
            password = self.connprofile["password"],
            host = self.connprofile["host"],
            port = self.connprofile["port"],
            tls = self.connprofile["tls"]
        )

        session._config = config

        def _():
            try:
                session.connect()
            except Exception as e:
                logger.error(e)
                sublime.error_message(
                    "Could not connect:\n Make sure the server is up\n\
                    and your credentials are correct."
                )
            # If the connect was successfull, save the credentials in memory for the session:
            profiles = get_setting("profiles")
            selected_profile_id = get_profile_index_by_name(profiles, self.connprofile["name"])
            profiles[selected_profile_id] = self.connprofile # pyright: ignore
            sublime.load_settings("Codemp.sublime-settings").set("profiles", profiles)

        sublime.set_timeout_async(_)


    def maybe_get_password(self):
        def __(pwd):
            self.connprofile["password"] = pwd
            self.update_config_and_connect()

        panel = self.window.show_input_panel("Password:", "", __, None, None)
        # Very undocumented feature, the input panel if has the setting 'password' set to true
        # will hide the input.
        panel.settings().set("password", True)

    def maybe_get_user(self):
        def __(usr):
            self.connprofile["username"] = usr
            self.maybe_get_password()

        panel = self.window.show_input_panel("Username:", "", __, None, None)
        panel.settings().set("password", False)

    def run(self):  # pyright: ignore[reportIncompatibleMethodOverride]

        def _run(index):
            profile = profiles[index]
            default_profile = get_default_profile(profiles)
            self.connprofile = {**default_profile, **profile}

            if self.connprofile["username"] and self.connprofile["password"]:
                self.update_config_and_connect()

            if not self.connprofile["username"] and not self.connprofile["password"]:
                self.maybe_get_user()

            if self.connprofile["username"] and not self.connprofile["password"]:
                self.maybe_get_password()

        profiles = valid_profiles(get_setting("profiles"))
        qplist = []
        for p in profiles:
            hint = f"host: {p['host']}:{p['port']}"
            user = p["username"]
            pwd = " : *******" if p["password"] else ""
            details = user + pwd
            qplist.append(qpi(p["name"], details, letter="P", hint=hint))

        show_qp(self.window, qplist, _run, placeholder="Profile")


# Disconnect Command
class CodempDisconnectCommand(sublime_plugin.WindowCommand):
    def is_enabled(self):
        return session.is_active()

    def run(self):
        cli = session.client

        for ws in workspaces.lookup():
            if cli.leave_workspace(ws.id):
                workspaces.remove(ws)

        session.drop_client()
        logger.info(f"disconnected from server '{session.config.host}'!")


# Join Workspace Command
class CodempJoinWorkspaceCommand(sublime_plugin.WindowCommand):
    def is_enabled(self) -> bool:
        return session.is_active()

    def input_description(self):
        return "Join:"

    def input(self, args):
        if "workspace_id" not in args:
            wslist = session.get_workspaces()
            return SimpleListInput(
                [("workspace_id", wslist)],
            )

    def run(self, workspace_id):  # pyright: ignore[reportIncompatibleMethodOverride]
        if workspace_id is None:
            return

        logger.info(f"Joining workspace: '{workspace_id}'...")
        try:
            ws = session.client.attach_workspace(workspace_id).wait()
        except Exception as e:
            logger.error(f"Could not join workspace '{workspace_id}': {e}")
            sublime.error_message(f"Could not join workspace '{workspace_id}'")
            raise e

        logger.debug("Joined! Adding workspace to registry")
        workspaces.register(ws)


# Leave Workspace Command
class CodempLeaveWorkspaceCommand(sublime_plugin.WindowCommand):
    def is_enabled(self):
        return session.is_active() and workspaces.hasactive()

    def input(self, args):
        if "workspace_id" not in args:
            return SimpleListInput(
                ("workspace_id", session.client.active_workspaces()),
            )

    def run(self, workspace_id: str):  # pyright: ignore[reportIncompatibleMethodOverride]
        try:
            workspaces.remove(workspace_id)
        finally:
            if not session.client.leave_workspace(workspace_id):
                logger.error(f"could not leave the workspace '{workspace_id}'")
            else:
                logger.debug(f"successfully left the workspace '{workspace_id}'")


class CodempInviteToWorkspaceCommand(sublime_plugin.WindowCommand):
    def is_enabled(self) -> bool:
        return session.is_active() and workspaces.hasactive() > 0

    def input(self, args):
        if "workspace_id" not in args:
            wslist = session.get_workspaces(owned=True, invited=False)
            return SimpleListInput(
                [("workspace_id", wslist), ("user", "invitee's username")]
            )

        if "user" not in args:
            return SimpleTextInput(("user", "invitee's username"))

    def run(self, workspace_id: str, user: str):  # pyright: ignore[reportIncompatibleMethodOverride]
        try:
            session.client.invite_to_workspace(workspace_id, user)
            logger.debug(f"invite sent to user {user} for workspace {workspace_id}.")
        except Exception as e:
            logger.error(f"Could not invite to workspace: {e}")



class CodempCreateWorkspaceCommand(sublime_plugin.WindowCommand):
    def is_enabled(self):
        return session.is_active()

    # def input(self, args):
    #     if "workspace_id" not in args:
    #         return SimpleTextInput(("workspace_id", "new workspace name"))

    def run(self, workspace_id: str):  # pyright: ignore[reportIncompatibleMethodOverride]
        try:
            session.client.create_workspace(workspace_id)
        except Exception as e:
            logger.error(f"Could not create workspace: {e}")



class CodempDeleteWorkspaceCommand(sublime_plugin.WindowCommand):
    def is_enabled(self):
        return session.is_active()

    # def input(self, args):
    #     workspaces = session.get_workspaces(owned=True, invited=False)  # noqa: F841
    #     if "workspace_id" not in args:
    #         return SimpleListInput(("workspace_id", workspaces))

    def run(self, workspace_id: str):  # pyright: ignore[reportIncompatibleMethodOverride]
        if workspace_id in workspaces:
            if not sublime.ok_cancel_dialog(
                "You are currently attached to '{workspace_id}'.\n\
                Do you want to detach and delete it?",
                ok_title="yes", title="Delete Workspace?",
            ): return
            self.window.run_command(
                "codemp_leave_workspace",
                {"workspace_id": workspace_id})
        else:
            if not sublime.ok_cancel_dialog(
                f"Confirm you want to delete the workspace '{workspace_id}'",
                ok_title="delete", title="Delete Workspace?",
            ): return

        session.client.delete_workspace(workspace_id).wait()