import datetime
import itertools
import os
import inspect
import shutil
from functools import wraps

from tornado import web

from traitlets import Unicode
from IPython.utils import tz
from notebook.utils import is_hidden, to_os_path, url_path_join
from notebook.services.contents.filemanager import FileContentsManager

from .manager import BundleManager
from ..nbxmanager import NBXContentsManager
from ..dispatch import DispatcherMixin
from .. import shim

def notebook_type_proxy(alt):
    """
    if notebook is a bundle, use regular method
    if notebook is file, defer to file manager
    """
    def decorator(meth):
        nonlocal alt
        meth_name = meth.__name__
        if alt is None:
            alt = meth_name
        argspec = inspect.getargspec(meth)

        @wraps(meth)
        def wrapper(self, *args, **kwargs):
            scope = kwargs.copy()
            # skip self in args
            scope.update(zip(argspec.args[1:], args))
            path = scope.get('path', '')

            method = meth.__get__(self)
            if self.notebook_type(path=path) == 'file':
                method = getattr(self.filemanager, alt)
            return method(*args, **kwargs)
        return wrapper
    return decorator

class BundleNotebookManager(NBXContentsManager):
    """
    """
    root_dir = Unicode()
    trash_dir = Unicode(config=True)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.bundler = BundleManager()
        self.filemanager = FileContentsManager(*args, **kwargs)
        self.filemanager.root_dir = self.root_dir

    def _get_os_path(self, path=''):
        return to_os_path(path, self.root_dir)

    def is_dir(self, path):
        return self.dir_exists(path) and not self.is_notebook(path)

    @notebook_type_proxy(alt=None)
    def get_kernel_path(self, path='', model=None):
        # get into bundle dir
        bundle_path = self.bundler._get_bundle_path(path)
        return os.path.join(self.root_dir, bundle_path)

    def dir_exists(self, path):
        path = path.strip('/')
        os_path = self._get_os_path(path=path)
        return os.path.isdir(os_path)

    def file_exists(self, path):
        os_path = self._get_os_path(path=path)
        return os.path.isfile(os_path)

    def get_file(self, path, content=True, **kwargs):
        os_path = self._get_os_path(path=path)
        return self.filemanager.get(path, content=content, **kwargs)

    def save_file(self, model, path=''):
        """Save the notebook model and return the model with no content."""

        model = self.filemanager.save(model, path)
        return model

    @notebook_type_proxy(alt='exists')
    def notebook_exists(self, path=''):
        return self._notebook_exists(path)

    def _notebook_exists(self, path):
        path = path.strip('/')
        os_path = self._get_os_path(path=path)
        return self.bundler.notebook_exists(os_path)

    def notebook_type(self, path=''):
        if self._notebook_exists(path):
            return 'bundle'
        if self.filemanager.exists(path) and path.endswith('ipynb'):
            return 'file'
        return None

    def is_hidden(self, path):
        return False

    def get_dir_model(self, path):
        model = {}
        model['name'] = path.rsplit('/', 1)[-1]
        model['path'] = path
        model['type'] = 'directory'
        return model

    def list_dirs(self, path):
        os_path = self._get_os_path(path=path)
        dirs = self.bundler.list_dirs(os_path)
        dirs = [self.get_dir_model(path) for path in dirs]
        return dirs

    def list_notebooks(self, path):
        os_path = self._get_os_path(path=path)
        bundles = self.bundler.list_bundles(os_path)
        notebooks = []
        for bundle_path, bundle in bundles.items():
            model = bundle.get_model(content=False)
            # the model returned from BundleManager is absolute
            # set back to relative
            model['path'] = os.path.join(path, bundle.name)
            notebooks.append(model)

        # also grab regular notebooks
        dir_model = self.filemanager.get(path=path, content=True)
        for model in dir_model['content']:
            if model['type'] == 'notebook':
                notebooks.append(model)

        return notebooks

    def list_files(self, path):
        os_path = self._get_os_path(path=path)
        notebooks = []

        # also grab regular notebooks
        dir_model = self.filemanager.get(path=path, content=True)
        for model in dir_model['content']:
            if model['type'] == 'file':
                notebooks.append(model)

        return notebooks


    @notebook_type_proxy(alt='get')
    def get_notebook(self, path='', content=True, file_content=False, **kwargs):
        path = path.strip('/')
        if not self.notebook_exists(path=path):
            raise Exception(
                'Notebook does not exist {path}'.format(
                    path=path
                )
            )
        os_path = self._get_os_path(path=path)
        bundle = self.bundler.get_notebook(os_path)
        model = bundle.get_model(content=content, file_content=file_content)
        model['path'] = path
        model['format'] = None
        if content:
            model['format'] = 'json'
        model['writable'] = True
        model['mimetype'] = None
        return model