"""Base class for notebook tests."""

from __future__ import print_function

import sys
import time
import requests
from contextlib import contextmanager
from subprocess import Popen, STDOUT
from unittest import TestCase

import nose

from IPython.utils.tempdir import TemporaryDirectory

MAX_WAITTIME = 30   # seconds to wait for notebook server to start
POLL_INTERVAL = 0.1 # time between attempts

# TimeoutError is a builtin on Python 3. This can be removed when we stop
# supporting Python 2.
class TimeoutError(Exception):
    pass

class NotebookTestBase(TestCase):
    """A base class for tests that need a running notebook.
    
    This creates an empty profile in a temp ipython_dir
    and then starts the notebook server with a separate temp notebook_dir.
    """

    port = 12341

    @classmethod
    def wait_until_alive(cls):
        """Wait for the server to be alive"""
        url = 'http://localhost:%i/api/notebooks' % cls.port
        for _ in range(int(MAX_WAITTIME/POLL_INTERVAL)):
            try:
                requests.get(url)
            except requests.exceptions.ConnectionError:
                if cls.notebook.poll() is not None:
                    raise RuntimeError("The notebook server exited with status %s" \
                                        % cls.notebook.poll())
                time.sleep(POLL_INTERVAL)
            else:
                return

        raise TimeoutError("The notebook server didn't start up correctly.")
    
    @classmethod
    def wait_until_dead(cls):
        """Wait for the server process to terminate after shutdown"""
        for _ in range(int(MAX_WAITTIME/POLL_INTERVAL)):
            if cls.notebook.poll() is not None:
                return
            time.sleep(POLL_INTERVAL)
    
        raise TimeoutError("Undead notebook server")

    @classmethod
    def setup_class(cls):
        cls.ipython_dir = TemporaryDirectory()
        cls.notebook_dir = TemporaryDirectory()
        notebook_args = [
            sys.executable, '-c',
            'from IPython.html.notebookapp import launch_new_instance; launch_new_instance()',
            '--port=%d' % cls.port,
            '--port-retries=0',  # Don't try any other ports
            '--no-browser',
            '--ipython-dir=%s' % cls.ipython_dir.name,
            '--notebook-dir=%s' % cls.notebook_dir.name,
        ]
        cls.notebook = Popen(notebook_args,
            stdout=nose.iptest_stdstreams_fileno(),
            stderr=STDOUT,
        )
        cls.wait_until_alive()

    @classmethod
    def teardown_class(cls):
        cls.notebook.terminate()
        cls.wait_until_dead()
        cls.ipython_dir.cleanup()
        cls.notebook_dir.cleanup()

    @classmethod
    def base_url(cls):
        return 'http://localhost:%i/' % cls.port


@contextmanager
def assert_http_error(status, msg=None):
    try:
        yield
    except requests.HTTPError as e:
        real_status = e.response.status_code
        assert real_status == status, \
                    "Expected status %d, got %d" % (real_status, status)
        if msg:
            assert msg in str(e), e
    else:
        assert False, "Expected HTTP error status"