from __future__ import annotations
from typing import Optional, Tuple
from typing import TYPE_CHECKING
if TYPE_CHECKING:
    import codemp

import sublime
import shutil
import tempfile
import logging
import gc

from codemp import Selection
from .. import globals as g
from ..utils import draw_cursor_region
from ..utils import bidict

from .session import session
from .buffers import buffers

logger = logging.getLogger(__name__)

def add_project_folder(w: sublime.Window, folder: str, name: str = ""):
    proj = w.project_data()
    if not isinstance(proj, dict):
        proj = {"folders": []}

    if name == "":
        entry = {"path": folder}
    else:
        entry = {"name": name, "path": folder}

    proj["folders"].append(entry)

    w.set_project_data(proj)

def remove_project_folder(w: sublime.Window, filterstr: str):
    proj: dict = w.project_data()  # type:ignore
    if proj is None:
        return

    clean_proj_folders = list(
        filter(
            lambda f: f.get("name", "") != filterstr,
            proj["folders"],
        )
    )
    proj["folders"] = clean_proj_folders
    w.set_project_data(proj)


def cursor_callback(ctl: codemp.CursorController):
    def _():
        while event := ctl.try_recv().wait():
            if event is None: break
            
            try: bfm = buffers.lookupId(event.sel.buffer)
            except KeyError: continue

            region_start = (event.sel.start_row, event.sel.start_col)
            region_end = (event.sel.end_row, event.sel.end_col)

            draw_cursor_region(bfm.view, region_start, region_end, event.user)
    sublime.set_timeout_async(_)

class WorkspaceManager():
    def __init__(self, handle: codemp.Workspace, window: sublime.Window, rootdir: str) -> None:
        self.handle: codemp.Workspace = handle
        self.window: sublime.Window = window
        self.curctl: codemp.CursorController = self.handle.cursor()
        self.rootdir: str = rootdir
        self.id: str = self.handle.id()
        self.curctl.callback(cursor_callback)

    def __del__(self):
        logger.debug(f"dropping workspace {self.id}")
        self.curctl.clear_callback()

        for buff in self.handle.active_buffers():
            if not self.handle.detach_buffer(buff):
                logger.warning(
                    f"could not detach from '{buff}' for workspace '{self.id}'."
                )

        for bfm in buffers.lookup(self):
            buffers.remove(bfm)

    def send_cursor(self, id: str, start: Tuple[int, int], end: Tuple[int, int]):
        # we can safely ignore the promise, we don't really care if everything
        # is ok for now with the cursor.
        sel = Selection(
            start_row=start[0],
            start_col=start[1],
            end_row=end[0],
            end_col=end[1],
            buffer=id
        )
        self.curctl.send(sel)

class WorkspaceRegistry():
    def __init__(self) -> None:
        self._workspaces: bidict[WorkspaceManager, sublime.Window] = bidict()

    def __contains__(self, item: str):
        try: self.lookupId(item)
        except KeyError: return False
        return True

    def hasactive(self):
        return len(session.client.active_workspaces()) > 0

    def lookup(self, w: Optional[sublime.Window] = None) -> list[WorkspaceManager]:
        if not w:
            return list(self._workspaces.keys())
        ws = self._workspaces.inverse.get(w)
        return ws if ws else []

    def lookupParent(self, ws: WorkspaceManager | str) -> sublime.Window:
        if isinstance(ws, str):
            wsm = self.lookupId(ws)
        return self._workspaces[ws]

    def lookupId(self, wid: str) -> WorkspaceManager:
        wsm = next((ws for ws in self._workspaces if ws.id == wid), None)
        if not wsm: raise KeyError
        return wsm

    def register(self, wshandle: codemp.Workspace) -> WorkspaceManager:
        win = sublime.active_window()

        # tmpdir = tempfile.mkdtemp(prefix="codemp_")
        # add_project_folder(win, tmpdir, f"{g.WORKSPACE_FOLDER_PREFIX}{wshandle.id()}")

        tmpdir = "DISABLED"
        wm = WorkspaceManager(wshandle, win, tmpdir)
        self._workspaces[wm] = win
        return wm

    def remove(self, ws: WorkspaceManager | str):
        if isinstance(ws, str):
            ws = self.lookupId(ws)

        # remove_project_folder(ws.window, f"{g.WORKSPACE_FOLDER_PREFIX}{ws.id}")
        # shutil.rmtree(ws.rootdir, ignore_errors=True)
        del self._workspaces[ws]



workspaces = WorkspaceRegistry()