import logging
from datetime import date
from decimal import Decimal

import tablib
from django.test import TestCase

from hordak.models import AccountType, StatementImport, StatementLine
from hordak.resources import StatementLineResource
from hordak.tests.utils import DataProvider


class StatementLineResourceTestCase(DataProvider, TestCase):
    """Test the resource definition in test_resources.py"""

    def setUp(self):
        self.account = self.account(is_bank_account=True, type=AccountType.asset)
        logging.disable(logging.CRITICAL)

    def tearDown(self):
        logging.disable(logging.INFO)

    def makeResource(self):
        statement_import = StatementImport.objects.create(
            bank_account=self.account, source="csv"
        )
        return StatementLineResource("%d/%m/%Y", statement_import)

    def test_import_one(self):
        dataset = tablib.Dataset(
            ["15/6/2016", "5.10", "Example payment"],
            headers=["date", "amount", "description"],
        )
        self.makeResource().import_data(dataset)

        self.assertEqual(StatementLine.objects.count(), 1)
        obj = StatementLine.objects.get()
        self.assertEqual(obj.date, date(2016, 6, 15))
        self.assertEqual(obj.amount, Decimal("5.10"))
        self.assertEqual(obj.description, "Example payment")

    def test_import_skip_duplicates(self):
        dataset = tablib.Dataset(
            ["15/6/2016", "5.10", "Example payment"],
            headers=["date", "amount", "description"],
        )
        self.makeResource().import_data(dataset)
        # Now do the import again
        self.makeResource().import_data(dataset)

        # The record in the second should have been ignored
        self.assertEqual(StatementLine.objects.count(), 1)

    def test_import_skip_duplicates_whitespace(self):
        dataset1 = tablib.Dataset(
            ["15/6/2016", "5.10", "Example payment"],
            headers=["date", "amount", "description"],
        )
        dataset2 = tablib.Dataset(
            ["15/6/2016", "5.10", "Example payment "],  # Whitespace added
            headers=["date", "amount", "description"],
        )

        self.makeResource().import_data(dataset1)
        self.makeResource().import_data(dataset2)

        # The record in the second should have been ignored
        self.assertEqual(StatementLine.objects.count(), 1)

    def test_import_two_identical(self):
        """Ensure they both get imported and that one doesnt get skipped as a duplicate

        After all, if there are two imported rows that look identical, it is probably because
        there are two identical transactions.
        """
        dataset = tablib.Dataset(
            ["15/6/2016", "5.10", "Example payment"],
            ["15/6/2016", "5.10", "Example payment"],
            headers=["date", "amount", "description"],
        )
        self.makeResource().import_data(dataset)

        self.assertEqual(StatementLine.objects.count(), 2)

    def test_import_a_few(self):
        dataset = tablib.Dataset(
            ["15/6/2016", "5.10", "Example payment"],
            ["16/6/2016", "10.91", "Another payment"],
            ["17/6/2016", "-1.23", "Paying someone"],
            headers=["date", "amount", "description"],
        )
        self.makeResource().import_data(dataset)

        self.assertEqual(StatementLine.objects.count(), 3)
        objs = StatementLine.objects.all().order_by("pk")

        self.assertEqual(objs[0].date, date(2016, 6, 15))
        self.assertEqual(objs[0].amount, Decimal("5.10"))
        self.assertEqual(objs[0].description, "Example payment")

        self.assertEqual(objs[1].date, date(2016, 6, 16))
        self.assertEqual(objs[1].amount, Decimal("10.91"))
        self.assertEqual(objs[1].description, "Another payment")

        self.assertEqual(objs[2].date, date(2016, 6, 17))
        self.assertEqual(objs[2].amount, Decimal("-1.23"))
        self.assertEqual(objs[2].description, "Paying someone")

    def test_import_a_few_with_identical_transactions(self):
        dataset = tablib.Dataset(
            ["15/6/2016", "5.10", "Example payment"],
            ["16/6/2016", "10.91", "Another payment"],
            ["16/6/2016", "10.91", "Another payment"],
            ["17/6/2016", "-1.23", "Paying someone"],
            headers=["date", "amount", "description"],
        )
        self.makeResource().import_data(dataset)

        self.assertEqual(StatementLine.objects.count(), 4)
        objs = StatementLine.objects.all().order_by("pk")

        self.assertEqual(objs[0].date, date(2016, 6, 15))
        self.assertEqual(objs[0].amount, Decimal("5.10"))
        self.assertEqual(objs[0].description, "Example payment")

        self.assertEqual(objs[1].date, date(2016, 6, 16))
        self.assertEqual(objs[1].amount, Decimal("10.91"))
        self.assertEqual(objs[1].description, "Another payment")

        self.assertEqual(objs[2].date, date(2016, 6, 16))
        self.assertEqual(objs[2].amount, Decimal("10.91"))
        self.assertEqual(objs[2].description, "Another payment")

        self.assertEqual(objs[3].date, date(2016, 6, 17))
        self.assertEqual(objs[3].amount, Decimal("-1.23"))
        self.assertEqual(objs[3].description, "Paying someone")

    def test_split_amounts(self):
        dataset = tablib.Dataset(
            ["15/6/2016", "", "100.56", "Example payment"],
            ["16/6/2016", "60.31", "", "Example income"],
            ["17/6/2016", "", "-102.56", "Example payment 2"],
            headers=["date", "amount_in", "amount_out", "description"],
        )
        self.makeResource().import_data(dataset)

        self.assertEqual(StatementLine.objects.count(), 3)

        obj = StatementLine.objects.all().order_by("date")
        self.assertEqual(obj[0].date, date(2016, 6, 15))
        self.assertEqual(obj[0].amount, Decimal("-100.56"))
        self.assertEqual(obj[0].description, "Example payment")

        self.assertEqual(obj[1].date, date(2016, 6, 16))
        self.assertEqual(obj[1].amount, Decimal("60.31"))
        self.assertEqual(obj[1].description, "Example income")

        self.assertEqual(obj[2].date, date(2016, 6, 17))
        self.assertEqual(obj[2].amount, Decimal("-102.56"))
        self.assertEqual(obj[2].description, "Example payment 2")

    def test_error_no_date(self):
        dataset = tablib.Dataset(
            ["5.10", "Example payment"], headers=["amount", "description"]
        )
        result = self.makeResource().import_data(dataset)
        self.assertEqual(len(result.row_errors()), 1)
        self.assertIn("No date", str(result.row_errors()[0][1][0].error))

    def test_error_empty_date(self):
        dataset = tablib.Dataset(
            ["", "5.10", "Example payment"], headers=["date", "amount", "description"]
        )
        result = self.makeResource().import_data(dataset)
        self.assertEqual(len(result.row_errors()), 1)
        self.assertIn("Expected dd/mm/yyyy", str(result.row_errors()[0][1][0].error))

    def test_error_empty_amounts(self):
        dataset = tablib.Dataset(
            ["15/6/2016", "", "", "Example payment"],
            headers=["date", "amount_in", "amount_out", "description"],
        )
        result = self.makeResource().import_data(dataset)
        self.assertEqual(len(result.row_errors()), 1)
        self.assertIn("Value required", str(result.row_errors()[0][1][0].error))

    def test_error_empty_amount(self):
        dataset = tablib.Dataset(
            ["15/6/2016", "", "Example payment"],
            headers=["date", "amount", "description"],
        )
        result = self.makeResource().import_data(dataset)
        self.assertEqual(len(result.row_errors()), 1)
        self.assertIn("No value found", str(result.row_errors()[0][1][0].error))

    def test_error_both_amounts(self):
        dataset = tablib.Dataset(
            ["15/6/2016", "5.10", "1.20", "Example payment"],
            headers=["date", "amount_in", "amount_out", "description"],
        )
        result = self.makeResource().import_data(dataset)
        self.assertEqual(len(result.row_errors()), 1)
        self.assertIn("Values found for both", str(result.row_errors()[0][1][0].error))

    def test_error_neither_amount(self):
        dataset = tablib.Dataset(
            ["15/6/2016", "", "", "Example payment"],
            headers=["date", "amount_in", "amount_out", "description"],
        )
        result = self.makeResource().import_data(dataset)
        self.assertEqual(len(result.row_errors()), 1)
        self.assertIn("either", str(result.row_errors()[0][1][0].error))

    def test_error_invalid_in_amount(self):
        dataset = tablib.Dataset(
            ["15/6/2016", "a", "", "Example payment"],
            headers=["date", "amount_in", "amount_out", "description"],
        )
        result = self.makeResource().import_data(dataset)
        self.assertEqual(len(result.row_errors()), 1)
        self.assertIn("Invalid", str(result.row_errors()[0][1][0].error))

    def test_error_invalid_out_amount(self):
        dataset = tablib.Dataset(
            ["15/6/2016", "", "a", "Example payment"],
            headers=["date", "amount_in", "amount_out", "description"],
        )
        result = self.makeResource().import_data(dataset)
        self.assertEqual(len(result.row_errors()), 1)
        self.assertIn("Invalid", str(result.row_errors()[0][1][0].error))

    def test_error_invalid_amount(self):
        dataset = tablib.Dataset(
            ["15/6/2016", "a", "Example payment"],
            headers=["date", "amount", "description"],
        )
        result = self.makeResource().import_data(dataset)
        self.assertEqual(len(result.row_errors()), 1)
        self.assertIn("Invalid", str(result.row_errors()[0][1][0].error))

    def test_error_no_amount(self):
        dataset = tablib.Dataset(
            ["15/6/2016", "Example payment"], headers=["date", "description"]
        )
        result = self.makeResource().import_data(dataset)
        self.assertEqual(len(result.row_errors()), 1)
        self.assertIn("No amount", str(result.row_errors()[0][1][0].error))

    def test_error_zero_amount(self):
        dataset = tablib.Dataset(
            ["15/6/2016", "0", "Example payment"],
            headers=["date", "amount", "description"],
        )
        result = self.makeResource().import_data(dataset)
        self.assertEqual(len(result.row_errors()), 1)
        self.assertIn("zero not allowed", str(result.row_errors()[0][1][0].error))
