from django.test import TestCase
from django.db import IntegrityError
from django.core.exceptions import ValidationError

from address.models import Country, State, Locality, Address, AddressField
from address.models import to_python

# Python 3 fixes.
import sys

if sys.version > "3":
    long = int
    basestring = (str, bytes)
    unicode = str


class CountryTestCase(TestCase):
    def setUp(self):
        self.au = Country.objects.create(name="Australia", code="AU")
        self.nz = Country.objects.create(name="New Zealand", code="NZ")
        self.be = Country.objects.create(name="Belgium", code="BE")

    def test_ordering(self):
        qs = Country.objects.all()
        self.assertEqual(qs.count(), 3)
        self.assertEqual(qs[0].code, "AU")
        self.assertEqual(qs[1].code, "BE")
        self.assertEqual(qs[2].code, "NZ")

    def test_unique_name(self):
        self.assertRaises(IntegrityError, Country.objects.create, name="Australia", code="**")

    def test_unicode(self):
        self.assertEqual(unicode(self.au), u"Australia")


class StateTestCase(TestCase):
    def setUp(self):
        self.au = Country.objects.create(name="Australia", code="AU")
        self.vic = State.objects.create(name="Victoria", code="VIC", country=self.au)
        self.tas = State.objects.create(name="Tasmania", code="TAS", country=self.au)
        self.qld = State.objects.create(name="Queensland", country=self.au)
        self.empty = State.objects.create(country=self.au)
        self.uk = Country.objects.create(name="United Kingdom", code="UK")
        self.uk_vic = State.objects.create(name="Victoria", code="VIC", country=self.uk)

    def test_required_country(self):
        self.assertRaises(IntegrityError, State.objects.create)

    def test_ordering(self):
        qs = State.objects.all()
        self.assertEqual(qs.count(), 5)
        self.assertEqual(qs[0].name, "")
        self.assertEqual(qs[1].name, "Queensland")
        self.assertEqual(qs[2].name, "Tasmania")
        self.assertEqual(qs[3].name, "Victoria")
        self.assertEqual(qs[4].name, "Victoria")

    def test_unique_name_country(self):
        State.objects.create(name="Tasmania", country=self.uk)
        self.assertRaises(IntegrityError, State.objects.create, name="Tasmania", country=self.au)

    def test_unicode(self):
        self.assertEqual(unicode(self.vic), u"Victoria, Australia")
        self.assertEqual(unicode(self.empty), u"Australia")


class LocalityTestCase(TestCase):
    def setUp(self):
        self.au = Country.objects.create(name="Australia", code="AU")
        self.uk = Country.objects.create(name="United Kingdom", code="UK")

        self.au_vic = State.objects.create(name="Victoria", code="VIC", country=self.au)
        self.au_tas = State.objects.create(name="Tasmania", code="TAS", country=self.au)
        self.au_qld = State.objects.create(name="Queensland", country=self.au)
        self.au_empty = State.objects.create(country=self.au)
        self.uk_vic = State.objects.create(name="Victoria", code="VIC", country=self.uk)

        self.au_vic_nco = Locality.objects.create(name="Northcote", postal_code="3070", state=self.au_vic)
        self.au_vic_mel = Locality.objects.create(name="Melbourne", postal_code="3000", state=self.au_vic)
        self.au_vic_ftz = Locality.objects.create(name="Fitzroy", state=self.au_vic)
        self.au_vic_empty = Locality.objects.create(state=self.au_vic)
        self.uk_vic_mel = Locality.objects.create(name="Melbourne", postal_code="3000", state=self.uk_vic)

    def test_required_state(self):
        self.assertRaises(IntegrityError, Locality.objects.create)

    def test_ordering(self):
        qs = Locality.objects.all()
        self.assertEqual(qs.count(), 5)
        self.assertEqual(qs[0].name, "")
        self.assertEqual(qs[1].name, "Fitzroy")
        self.assertEqual(qs[2].name, "Melbourne")
        self.assertEqual(qs[3].name, "Northcote")
        self.assertEqual(qs[4].name, "Melbourne")

    def test_unicode(self):
        self.assertEqual(unicode(self.au_vic_mel), u"Melbourne, Victoria 3000, Australia")
        self.assertEqual(unicode(self.au_vic_ftz), u"Fitzroy, Victoria, Australia")
        self.assertEqual(unicode(self.au_vic_empty), u"Victoria, Australia")


class AddressTestCase(TestCase):
    def setUp(self):
        self.au = Country.objects.create(name="Australia", code="AU")
        self.uk = Country.objects.create(name="United Kingdom", code="UK")

        self.au_vic = State.objects.create(name="Victoria", code="VIC", country=self.au)
        self.au_tas = State.objects.create(name="Tasmania", code="TAS", country=self.au)
        self.au_qld = State.objects.create(name="Queensland", country=self.au)
        self.au_empty = State.objects.create(country=self.au)
        self.uk_vic = State.objects.create(name="Victoria", code="VIC", country=self.uk)

        self.au_vic_nco = Locality.objects.create(name="Northcote", postal_code="3070", state=self.au_vic)
        self.au_vic_mel = Locality.objects.create(name="Melbourne", postal_code="3000", state=self.au_vic)
        self.au_vic_ftz = Locality.objects.create(name="Fitzroy", state=self.au_vic)
        self.au_vic_empty = Locality.objects.create(state=self.au_vic)
        self.uk_vic_mel = Locality.objects.create(name="Melbourne", postal_code="3000", state=self.uk_vic)

        self.ad1 = Address.objects.create(
            street_number="1",
            route="Some Street",
            locality=self.au_vic_mel,
            raw="1 Some Street, Victoria, Melbourne",
        )
        self.ad2 = Address.objects.create(
            street_number="10",
            route="Other Street",
            locality=self.au_vic_mel,
            raw="10 Other Street, Victoria, Melbourne",
        )
        self.ad3 = Address.objects.create(
            street_number="1",
            route="Some Street",
            locality=self.au_vic_nco,
            raw="1 Some Street, Northcote, Victoria",
        )
        self.ad_empty = Address.objects.create(locality=self.au_vic_nco, raw="Northcote, Victoria")

    def test_required_raw(self):
        obj = Address.objects.create()
        self.assertRaises(ValidationError, obj.clean)

    def test_ordering(self):
        qs = Address.objects.all()
        self.assertEqual(qs.count(), 4)
        self.assertEqual(qs[0].route, "Other Street")
        self.assertEqual(qs[1].route, "Some Street")
        self.assertEqual(qs[2].route, "")
        self.assertEqual(qs[3].route, "Some Street")

    # def test_unique_street_address_locality(self):
    #     Address.objects.create(street_number='10', route='Other Street', locality=self.au_vic_nco)
    #     self.assertRaises(
    #         IntegrityError, Address.objects.create,
    #         street_number='10', route='Other Street', locality=self.au_vic_mel
    #     )

    def test_unicode(self):
        self.assertEqual(unicode(self.ad1), u"1 Some Street, Melbourne, Victoria 3000, Australia")
        self.assertEqual(unicode(self.ad_empty), u"Northcote, Victoria 3070, Australia")


class AddressFieldTestCase(TestCase):
    class TestModel(object):
        address = AddressField()

    def setUp(self):
        self.ad1_dict = {
            "raw": "1 Somewhere Street, Northcote, Victoria 3070, VIC, AU",
            "street_number": "1",
            "route": "Somewhere Street",
            "locality": "Northcote",
            "postal_code": "3070",
            "state": "Victoria",
            "state_code": "VIC",
            "country": "Australia",
            "country_code": "AU",
        }
        self.test = self.TestModel()

    def test_assignment_from_dict(self):
        self.test.address = to_python(self.ad1_dict)
        self.assertEqual(self.test.address.raw, self.ad1_dict["raw"])
        self.assertEqual(self.test.address.street_number, self.ad1_dict["street_number"])
        self.assertEqual(self.test.address.route, self.ad1_dict["route"])
        self.assertEqual(self.test.address.locality.name, self.ad1_dict["locality"])
        self.assertEqual(self.test.address.locality.postal_code, self.ad1_dict["postal_code"])
        self.assertEqual(self.test.address.locality.state.name, self.ad1_dict["state"])
        self.assertEqual(self.test.address.locality.state.code, self.ad1_dict["state_code"])
        self.assertEqual(self.test.address.locality.state.country.name, self.ad1_dict["country"])
        self.assertEqual(self.test.address.locality.state.country.code, self.ad1_dict["country_code"])

    def test_assignment_from_dict_no_country(self):
        ad = {
            "raw": "1 Somewhere Street, Northcote, Victoria 3070, VIC, AU",
            "street_number": "1",
            "route": "Somewhere Street",
            "locality": "Northcote",
            "state": "Victoria",
        }
        self.test.address = to_python(ad)
        self.assertEqual(self.test.address.raw, ad["raw"])
        self.assertEqual(self.test.address.street_number, "")
        self.assertEqual(self.test.address.route, "")
        self.assertEqual(self.test.address.locality, None)

    def test_assignment_from_dict_no_state(self):
        ad = {
            "raw": "Somewhere",
            "locality": "Northcote",
            "country": "Australia",
        }
        self.test.address = to_python(ad)
        self.assertEqual(self.test.address.raw, ad["raw"])
        self.assertEqual(self.test.address.street_number, "")
        self.assertEqual(self.test.address.route, "")
        self.assertEqual(self.test.address.locality, None)

    def test_assignment_from_dict_no_locality(self):
        ad = {
            "raw": "1 Somewhere Street, Northcote, Victoria 3070, VIC, AU",
            "street_number": "1",
            "route": "Somewhere Street",
            "state": "Victoria",
            "country": "Australia",
        }
        self.test.address = to_python(ad)
        self.assertEqual(self.test.address.raw, ad["raw"])
        self.assertEqual(self.test.address.street_number, "")
        self.assertEqual(self.test.address.route, "")
        self.assertEqual(self.test.address.locality, None)

    def test_assignment_from_dict_only_address(self):
        ad = {
            "raw": "1 Somewhere Street, Northcote, Victoria 3070, VIC, AU",
            "street_number": "1",
            "route": "Somewhere Street",
        }
        self.test.address = to_python(ad)
        self.assertEqual(self.test.address.raw, ad["raw"])
        self.assertEqual(self.test.address.street_number, ad["street_number"])
        self.assertEqual(self.test.address.route, ad["route"])
        self.assertEqual(self.test.address.locality, None)

    def test_assignment_from_dict_duplicate_country_code(self):
        ad = {
            "raw": "1 Somewhere Street, Northcote, Victoria 3070, VIC, AU",
            "street_number": "1",
            "route": "Somewhere Street",
            "locality": "Northcote",
            "state": "Victoria",
            "country": "Australia",
            "country_code": "Australia",
        }
        self.test.address = to_python(ad)
        self.assertEqual(self.test.address.raw, ad["raw"])
        self.assertEqual(self.test.address.street_number, "1")
        self.assertEqual(self.test.address.route, "Somewhere Street")
        self.assertEqual(self.test.address.locality.name, "Northcote")
        self.assertEqual(self.test.address.locality.state.name, "Victoria")
        self.assertEqual(self.test.address.locality.state.country.name, "Australia")
        self.assertEqual(self.test.address.locality.state.country.code, "")

    def test_assignment_from_dict_duplicate_state_code(self):
        ad = {
            "raw": "1 Somewhere Street, Northcote, Victoria 3070, VIC, AU",
            "street_number": "1",
            "route": "Somewhere Street",
            "locality": "Northcote",
            "state": "Victoria",
            "state_code": "Victoria",
            "country": "Australia",
        }
        self.test.address = to_python(ad)
        self.assertEqual(self.test.address.raw, ad["raw"])
        self.assertEqual(self.test.address.street_number, "1")
        self.assertEqual(self.test.address.route, "Somewhere Street")
        self.assertEqual(self.test.address.locality.name, "Northcote")
        self.assertEqual(self.test.address.locality.state.name, "Victoria")
        self.assertEqual(self.test.address.locality.state.code, "Victoria")
        self.assertEqual(self.test.address.locality.state.country.name, "Australia")

    def test_assignment_from_dict_invalid_country_code(self):
        ad = {
            "raw": "1 Somewhere Street, Northcote, Victoria 3070, VIC, AU",
            "street_number": "1",
            "route": "Somewhere Street",
            "locality": "Northcote",
            "state": "Victoria",
            "country": "Australia",
            "country_code": "Something else",
        }
        self.assertRaises(ValueError, to_python, ad)

    def test_assignment_from_dict_invalid_state_code(self):
        ad = {
            "raw": "1 Somewhere Street, Northcote, Victoria 3070, VIC, AU",
            "street_number": "1",
            "route": "Somewhere Street",
            "locality": "Northcote",
            "state": "Victoria",
            "state_code": "Something",
            "country": "Australia",
        }
        # This is invalid because state codes are expected to have a max of 8 characters
        self.assertRaises(ValueError, to_python, ad)

    def test_assignment_from_string(self):
        self.test.address = to_python(self.ad1_dict["raw"])
        self.assertEqual(self.test.address.raw, self.ad1_dict["raw"])

    # def test_save(self):
    #     self.test.address = self.ad1_dict
    #     self.test.save()
    #     test = self.TestModel.objects.all()[0]
    #     self.assertEqual(test.address.raw, self.ad1_dict['raw'])
    #     self.assertEqual(test.address.street_number, self.ad1_dict['street_number'])
    #     self.assertEqual(test.address.route, self.ad1_dict['route'])
    #     self.assertEqual(test.address.locality.name, self.ad1_dict['locality'])
    #     self.assertEqual(test.address.locality.postal_code, self.ad1_dict['postal_code'])
    #     self.assertEqual(test.address.locality.state.name, self.ad1_dict['state'])
    #     self.assertEqual(test.address.locality.state.code, self.ad1_dict['state_code'])
    #     self.assertEqual(test.address.locality.state.country.name, self.ad1_dict['country'])
    #     self.assertEqual(test.address.locality.state.country.code, self.ad1_dict['country_code'])
