"""Testing utils for jupyter_client tests

"""
import json
import os
import sys
from typing import Dict

import pytest

from jupyter_client import (
    AsyncKernelManager,
    AsyncMultiKernelManager,
    KernelManager,
    MultiKernelManager,
)

pjoin = os.path.join

skip_win32 = pytest.mark.skipif(sys.platform.startswith("win"), reason="Windows")


sample_kernel_json = {
    "argv": ["cat", "{connection_file}"],
    "display_name": "Test kernel",
}


def install_kernel(kernels_dir, argv=None, name="test", display_name=None):
    """install a kernel in a kernels directory"""
    kernel_dir = pjoin(kernels_dir, name)
    os.makedirs(kernel_dir)
    kernel_json = {
        "argv": argv or sample_kernel_json["argv"],
        "display_name": display_name or sample_kernel_json["display_name"],
    }
    json_file = pjoin(kernel_dir, "kernel.json")
    with open(json_file, "w") as f:
        json.dump(kernel_json, f)
    return kernel_dir


class RecordCallMixin:
    method_calls: Dict[str, int]

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.method_calls = {}

    def record(self, method_name: str) -> None:
        if method_name not in self.method_calls:
            self.method_calls[method_name] = 0
        self.method_calls[method_name] += 1

    def call_count(self, method_name: str) -> int:
        if method_name not in self.method_calls:
            self.method_calls[method_name] = 0
        return self.method_calls[method_name]

    def reset_counts(self) -> None:
        for record in self.method_calls:
            self.method_calls[record] = 0


def subclass_recorder(f):
    def wrapped(self, *args, **kwargs):
        # record this call
        self.record(f.__name__)
        method = getattr(self._superclass, f.__name__)
        # call the superclass method
        r = method(self, *args, **kwargs)
        # call anything defined in the actual class method
        f(self, *args, **kwargs)
        return r

    return wrapped


class KMSubclass(RecordCallMixin):
    @subclass_recorder
    def start_kernel(self, **kw):
        """Record call and defer to superclass"""

    @subclass_recorder
    def shutdown_kernel(self, now=False, restart=False):
        """Record call and defer to superclass"""

    @subclass_recorder
    def _async_shutdown_kernel(self, now=False, restart=False):
        """Record call and defer to superclass"""

    @subclass_recorder
    def restart_kernel(self, now=False, **kw):
        """Record call and defer to superclass"""

    @subclass_recorder
    def interrupt_kernel(self):
        """Record call and defer to superclass"""

    @subclass_recorder
    def _async_interrupt_kernel(self):
        """Record call and defer to superclass"""

    @subclass_recorder
    def request_shutdown(self, restart=False):
        """Record call and defer to superclass"""

    @subclass_recorder
    def finish_shutdown(self, waittime=None, pollinterval=0.1, restart=False):
        """Record call and defer to superclass"""

    @subclass_recorder
    def _async_launch_kernel(self, kernel_cmd, **kw):
        """Record call and defer to superclass"""

    @subclass_recorder
    def _async_kill_kernel(self):
        """Record call and defer to superclass"""

    @subclass_recorder
    def cleanup_resources(self, restart=False):
        """Record call and defer to superclass"""

    @subclass_recorder
    def _async_cleanup_resources(self, restart=False):
        """Record call and defer to superclass"""

    @subclass_recorder
    def signal_kernel(self, signum):
        """Record call and defer to superclass"""

    @subclass_recorder
    def _async_signal_kernel(self, signum):
        """Record call and defer to superclass"""

    @subclass_recorder
    def is_alive(self):
        """Record call and defer to superclass"""

    @subclass_recorder
    def _async_is_alive(self):
        """Record call and defer to superclass"""

    @subclass_recorder
    def _async_send_kernel_sigterm(self, restart=False):
        """Record call and defer to superclass"""


class SyncKMSubclass(KMSubclass, KernelManager):
    """Used to test subclass hierarchies to ensure methods are called when expected."""

    _superclass = KernelManager


class AsyncKMSubclass(KMSubclass, AsyncKernelManager):
    """Used to test subclass hierarchies to ensure methods are called when expected."""

    _superclass = AsyncKernelManager


class MKMSubclass(RecordCallMixin):
    def _kernel_manager_class_default(self):
        return "tests.utils.SyncKMSubclass"

    @subclass_recorder
    def get_kernel(self, kernel_id):
        """Record call and defer to superclass"""

    @subclass_recorder
    def remove_kernel(self, kernel_id):
        """Record call and defer to superclass"""

    @subclass_recorder
    def start_kernel(self, *, kernel_name=None, **kwargs):
        """Record call and defer to superclass"""

    @subclass_recorder
    def _async_start_kernel(self, *, kernel_name=None, **kwargs):
        """Record call and defer to superclass"""

    @subclass_recorder
    def shutdown_kernel(self, kernel_id, now=False, restart=False):
        """Record call and defer to superclass"""

    @subclass_recorder
    def restart_kernel(self, kernel_id, now=False):
        """Record call and defer to superclass"""

    @subclass_recorder
    def interrupt_kernel(self, kernel_id):
        """Record call and defer to superclass"""

    @subclass_recorder
    def request_shutdown(self, kernel_id, restart=False):
        """Record call and defer to superclass"""

    @subclass_recorder
    def _async_request_shutdown(self, kernel_id, restart=False):
        """Record call and defer to superclass"""

    @subclass_recorder
    def finish_shutdown(self, kernel_id, waittime=None, pollinterval=0.1, restart=False):
        """Record call and defer to superclass"""

    @subclass_recorder
    def _async_finish_shutdown(self, kernel_id, waittime=None, pollinterval=0.1, restart=False):
        """Record call and defer to superclass"""

    @subclass_recorder
    def cleanup_resources(self, kernel_id, restart=False):
        """Record call and defer to superclass"""

    @subclass_recorder
    def shutdown_all(self, now=False):
        """Record call and defer to superclass"""


class SyncMKMSubclass(MKMSubclass, MultiKernelManager):
    _superclass = MultiKernelManager

    def _kernel_manager_class_default(self):
        return "tests.utils.SyncKMSubclass"


class AsyncMKMSubclass(MKMSubclass, AsyncMultiKernelManager):
    _superclass = AsyncMultiKernelManager

    def _kernel_manager_class_default(self):
        return "tests.utils.AsyncKMSubclass"
