import os
import urllib.request, urllib.parse, urllib.error
import html5lib
import datetime
from django.urls import reverse
from django.core.management import call_command

from django.test import TestCase

# course with the test data
TEST_COURSE_SLUG = '2020su-cmpt-120-d1'

# when roles can reasonably expire for tests
TEST_ROLE_EXPIRY = datetime.date.today() + datetime.timedelta(days=365)

class CachedFixtureTestCase(TestCase):
    current_fixtures = None

    def _pre_setup(self):
        super(CachedFixtureTestCase, self)._pre_setup()

    def _post_teardown(self):
        super(CachedFixtureTestCase, self)._post_teardown()

    def _remove_fixture(self):
        for db_name in self._databases_names(include_mirrors=False):
             call_command('flush', verbosity=0, interactive=False,
                            database=db_name, skip_checks=True,
                            reset_sequences=False,
                            allow_cascade=self.available_apps is not None,
                            inhibit_post_migrate=self.available_apps is not None)

    def _fixture_setup(self):
        for db_name in self._databases_names(include_mirrors=False):
            if (hasattr(self, 'fixtures') and 
                      self.fixtures and 
                      self.fixtures != CachedFixtureTestCase.current_fixtures):
                print(self.fixtures)
                CachedFixtureTestCase.current_fixture = self.fixtures
                self._remove_fixture()
                call_command('loaddata', *self.fixtures,
                   **{'verbosity': 0, 'database': db_name, 'skip_checks': True})
            else:
                self._remove_fixture()

    def _fixture_teardown(self):
        print("teardown pass")
        pass


def validate_content(testcase, data, page_descr="unknown page"):
    """
    Validate data as HTML5.

    testcase should be a unittest.TestCase object (or similar).
    page_descr should be a human-readable description of the page being tested.
    """
    parser = html5lib.HTMLParser(tree=html5lib.treebuilders.getTreeBuilder("dom"))
    parser.parse(data)
    if parser.errors:
        fh = open("tmp-validation.html", "wb")
        fh.write(data)
        fh.close()
        testcase.fail("Invalid HTML5 produced in %s:\n  %s" % (page_descr, str(parser.errors)))



def basic_page_tests(testcase, client, url, check_valid=True):
    """
    Run basic tests on the page: 200 OK, validity.
    """
    response = client.get(url)
    testcase.assertEqual(response.status_code, 200)
    if check_valid:
        validate_content(testcase, response.content, url)
    return response


def test_views(testcase, client, view_prefix, views, url_args, qs=None):
    """
    Test a collection of views, just to make sure they render
    """
    for v in views:
        view = view_prefix + v
        try:
            url = reverse(view, kwargs=url_args)
            if qs:
                url += '?' + qs
            response = basic_page_tests(testcase, client, url)
        except Exception as e:
            print("failing with view=%s; kwargs=%s" % (view, url_args))
            raise



from django.conf import settings
from django.contrib.auth.models import User
from importlib import import_module
from django.http import HttpRequest
from django.contrib.auth import login
from django.test.client import Client as OriginalClient


# Adapted from http://jameswestby.net/weblog/tech/17-directly-logging-in-a-user-in-django-tests.html
# adds the login_user method that just logs some existing user in.
class Client(OriginalClient):
    def login_user(self, userid):
        """
        Login as specified user, does not depend on auth backend (hopefully)

        This is based on Client.login() with a small hack that does not
        require the call to authenticate()
        """
        if not 'django.contrib.sessions' in settings.INSTALLED_APPS:
            raise AssertionError("Unable to login without django.contrib.sessions in INSTALLED_APPS")
        try:
            user = User.objects.get(username=userid)
        except User.DoesNotExist:
            user = User(username=userid, password='')
            user.save()
        user.backend = "%s.%s" % ("django.contrib.auth.backends",
                                  "ModelBackend")
        engine = import_module(settings.SESSION_ENGINE)

        # Create a fake request to store login details.
        request = HttpRequest()
        #if self.session:
        #    request.session = self.session
        #else:
        request.session = engine.SessionStore()
        login(request, user)

        # Set the cookie to represent the session.
        session_cookie = settings.SESSION_COOKIE_NAME
        self.cookies[session_cookie] = request.session.session_key
        cookie_data = {
            'max-age': None,
            'path': '/',
            'domain': settings.SESSION_COOKIE_DOMAIN,
            'secure': settings.SESSION_COOKIE_SECURE or None,
            'expires': None,
        }
        self.cookies[session_cookie].update(cookie_data)

        # Save the session values.
        request.session.save()

from coredata.models import Semester, SemesterWeek
import datetime
def create_fake_semester(strm):
    """
    Create a close-enough Semester object for testing
    """
    strm = str(strm)
    if Semester.objects.filter(name=strm):
        return
    s = Semester(name=strm)
    yr = int(strm[0:3]) + 1900
    if strm[3] == '1':
        mo = 1
    elif strm[3] == '4':
        mo = 5
    elif strm[3] == '7':
        mo = 9

    s.start = datetime.date(yr,mo,5)
    s.end = datetime.date(yr,mo+3,1)
    s.save()

    sw = SemesterWeek(semester=s, week=1)
    mon = s.start
    while mon.weekday() != 0:
        mon -= datetime.timedelta(days=1)
    sw.monday = mon
    sw.save()

    return s


from coredata.models import CourseOffering, Unit, Person, Member
def create_test_offering():
    """
    Create a CourseOffering (and related stuff) that can be used in tests with no fixtures
    """
    s = create_fake_semester('1144')
    u = Unit(label='BABL', name="Department of Babbling")
    u.save()
    o = CourseOffering(subject='BABL', number='123', section='F104', semester=s, component='LEC', owner=u,
                       title='Babbling for Baferad Ferzizzles', enrl_cap=100, enrl_tot=5, wait_tot=0)
    o.save()

    i = Person(first_name='Insley', last_name='Instructorberg', emplid=20000009, userid='instr')
    i.save()
    s = Person(first_name='Stanley', last_name='Studentson', emplid=20000010, userid='student')
    s.save()

    Member(offering=o, person=i, role='INST').save()
    Member(offering=o, person=s, role='STUD').save()

    return o