from django.test import TestCase

from wagtail import blocks
from wagtail.contrib.typed_table_block.blocks import (
    TypedTable, TypedTableBlock, TypedTableBlockValidationError)


class CountryChoiceBlock(blocks.ChoiceBlock):
    """A ChoiceBlock with a custom rendering, to check that block rendering is honoured"""
    def render_basic(self, value, context=None):
        return value.upper() if value else value


class TestTableBlock(TestCase):
    def setUp(self):
        self.block = TypedTableBlock([
            ('text', blocks.CharBlock()),
            ('country', CountryChoiceBlock(choices=[
                ('be', 'Belgium'),
                ('fr', 'France'),
                ('nl', 'Netherlands'),
            ])),
        ])

        self.form_data = {
            'table-column-count': '2',
            'table-row-count': '3',
            'table-column-0-type': 'country',
            'table-column-0-order': '0',
            'table-column-0-deleted': '',
            'table-column-0-heading': 'Country',
            'table-column-1-type': 'text',
            'table-column-1-order': '1',
            'table-column-1-deleted': '',
            'table-column-1-heading': 'Description',
            'table-row-0-order': '1',
            'table-row-0-deleted': '',
            'table-cell-0-0': 'fr',
            'table-cell-0-1': 'A large country with baguettes',
            'table-row-1-order': '0',
            'table-row-1-deleted': '',
            'table-cell-1-0': 'nl',
            'table-cell-1-1': 'A small country with stroopwafels',
            'table-row-2-order': '2',
            'table-row-2-deleted': '1',
            'table-cell-2-0': 'be',
            'table-cell-2-1': 'A small country with sprouts',
        }

        self.db_data = {
            'columns': [
                {'type': 'country', 'heading': 'Country'},
                {'type': 'text', 'heading': 'Description'},
            ],
            'rows': [
                {'values': ['nl', 'A small country with stroopwafels']},
                {'values': ['fr', 'A large country with baguettes']},
            ],
        }

    def test_value_from_datadict(self):
        """
        Test that we can turn posted form data into a TypedTable instance,
        accounting for row reordering and deleted rows.
        """
        table = self.block.value_from_datadict(self.form_data, {}, 'table')

        self.assertIsInstance(table, TypedTable)
        self.assertEqual(len(table.columns), 2)
        self.assertEqual(table.columns[0]['heading'], 'Country')
        self.assertEqual(table.columns[1]['heading'], 'Description')
        rows = list(table.rows)
        self.assertEqual(len(rows), 2)
        self.assertEqual([block.value for block in rows[0]], ['nl', 'A small country with stroopwafels'])
        self.assertEqual([block.value for block in rows[1]], ['fr', 'A large country with baguettes'])

    def test_submission_with_column_deletion_and_insertion(self):
        # Test server-side behaviour in the setting described in
        # https://github.com/wagtail/wagtail/issues/7654

        # This form data represents a submission where there are three columns:
        # country (column id 0), population (column id 3), description (column id 2)
        # Column id 1 is a population column that was deleted before being replaced by the
        # current one with id 3.
        form_data = {
            # table-column-count includes deleted columns, as it's telling the server code
            # the maximum column ID number it should consider
            'table-column-count': '4',
            'table-row-count': '1',
            'table-column-0-type': 'country',
            'table-column-0-order': '0',
            'table-column-0-deleted': '',
            'table-column-0-heading': 'Country',
            'table-column-1-deleted': '1',
            'table-column-2-type': 'text',
            'table-column-2-order': '2',
            'table-column-2-deleted': '',
            'table-column-2-heading': 'Description',
            'table-column-3-type': 'text',
            'table-column-3-order': '1',
            'table-column-3-deleted': '',
            'table-column-3-heading': 'Population',
            'table-row-0-order': '1',
            'table-row-0-deleted': '',
            'table-cell-0-0': 'fr',
            'table-cell-0-3': '68000000',
            'table-cell-0-2': 'A large country with baguettes',
        }
        table = self.block.value_from_datadict(form_data, {}, 'table')

        self.assertIsInstance(table, TypedTable)
        self.assertEqual(len(table.columns), 3)
        self.assertEqual(table.columns[0]['heading'], 'Country')
        self.assertEqual(table.columns[1]['heading'], 'Population')
        self.assertEqual(table.columns[2]['heading'], 'Description')
        rows = list(table.rows)
        self.assertEqual(len(rows), 1)
        self.assertEqual(
            [block.value for block in rows[0]],
            ['fr', '68000000', 'A large country with baguettes']
        )

    def test_to_python(self):
        """
        Test that we can turn JSONish data from the database into a TypedTable instance
        """
        table = self.block.to_python(self.db_data)
        self.assertIsInstance(table, TypedTable)
        self.assertEqual(len(table.columns), 2)
        self.assertEqual(table.columns[0]['heading'], 'Country')
        self.assertEqual(table.columns[1]['heading'], 'Description')
        rows = list(table.rows)
        self.assertEqual(len(rows), 2)
        self.assertEqual([block.value for block in rows[0]], ['nl', 'A small country with stroopwafels'])
        self.assertEqual([block.value for block in rows[1]], ['fr', 'A large country with baguettes'])

    def test_get_prep_value(self):
        """
        Test that we can turn a TypedTable instance into JSONish data for the database
        """
        table = self.block.value_from_datadict(self.form_data, {}, 'table')
        table_data = self.block.get_prep_value(table)
        self.assertEqual(table_data, self.db_data)

    def test_clean(self):
        table = self.block.value_from_datadict(self.form_data, {}, 'table')
        # cleaning a valid table should return a TypedTable instance
        cleaned_table = self.block.clean(table)
        self.assertIsInstance(cleaned_table, TypedTable)

        # now retry with invalid data (description is a required field)
        invalid_form_data = self.form_data.copy()
        invalid_form_data['table-cell-0-1'] = ''
        invalid_table = self.block.value_from_datadict(invalid_form_data, {}, 'table')

        with self.assertRaises(TypedTableBlockValidationError) as exc_info:
            self.block.clean(invalid_table)

        # table-cell-0-1 is actually cell 1 of row 1 due to the swapped row order in the data
        self.assertTrue(exc_info.exception.cell_errors[1][1])

    def test_render(self):
        table = self.block.value_from_datadict(self.form_data, {}, 'table')
        html = self.block.render(table)

        self.assertIn('<th scope="col">Country</th>', html)
        # rendering should use the block renderings of the child blocks ('FR' not 'fr')
        self.assertIn('<td>FR</td>', html)
