# -*- coding: utf-8; -*-

import datetime
from unittest.mock import patch, MagicMock

from wuttjamaican.reports import Report

import colander
from pyramid.httpexceptions import HTTPNotFound

from wuttaweb.views import reports as mod
from wuttaweb.testing import WebTestCase


class SomeRandomReport(Report):
    """
    This report shows something random.
    """
    report_key = 'testing_some_random'
    report_title = "Random Test Report"

    def add_params(self, schema):

        schema.add(colander.SchemaNode(
            colander.String(),
            name='foo',
            missing=colander.null))

        schema.add(colander.SchemaNode(
            colander.Date(),
            name='start_date',
            missing=colander.null))

    def get_output_columns(self):
        return ['foo']

    def make_data(self, params, **kwargs):
        return {
            'output_title': "Testing Output",
            'data': [{'foo': 'bar'}],
        }


class TestReportViews(WebTestCase):

    def make_view(self):
        return mod.ReportView(self.request)

    def test_includeme(self):
        self.pyramid_config.include('wuttaweb.views.reports')

    def test_get_grid_data(self):
        view = self.make_view()
        providers = dict(self.app.providers)
        providers['wuttatest'] = MagicMock(report_modules=['tests.views.test_reports'])
        with patch.object(self.app, 'providers', new=providers):

            data = view.get_grid_data()
            self.assertIsInstance(data, list)
            self.assertTrue(data) # 1+ reports

    def test_normalize_report(self):
        view = self.make_view()
        report = SomeRandomReport(self.config)
        normal = view.normalize_report(report)
        help_text = normal.pop('help_text').strip()
        self.assertEqual(help_text, "This report shows something random.")
        self.assertEqual(normal, {
            'report_key': 'testing_some_random',
            'report_title': "Random Test Report",
        })

    def test_configure_grid(self):
        view = self.make_view()
        grid = view.make_model_grid()
        self.assertIn('report_title', grid.searchable_columns)
        self.assertIn('help_text', grid.searchable_columns)

    def test_get_instance(self):
        view = self.make_view()
        providers = {
            'wuttatest': MagicMock(report_modules=['tests.views.test_reports']),
        }
        with patch.object(self.app, 'providers', new=providers):

            # normal
            with patch.object(self.request, 'matchdict', new={'report_key': 'testing_some_random'}):
                report = view.get_instance()
                self.assertIsInstance(report, dict)
                self.assertEqual(report['report_key'], 'testing_some_random')
                self.assertEqual(report['report_title'], "Random Test Report")

            # not found
            with patch.object(self.request, 'matchdict', new={'report_key': 'this-should_notEXIST'}):
                self.assertRaises(HTTPNotFound, view.get_instance)

    def test_get_instance_title(self):
        view = self.make_view()
        result = view.get_instance_title({'report_title': 'whatever'})
        self.assertEqual(result, 'whatever')

    def test_view(self):
        self.pyramid_config.add_route('home', '/')
        self.pyramid_config.add_route('login', '/auth/login')
        self.pyramid_config.add_route('reports', '/reports/')
        self.pyramid_config.add_route('reports.view', '/reports/{report_key}')
        view = self.make_view()
        providers = dict(self.app.providers)
        providers['wuttatest'] = MagicMock(report_modules=['tests.views.test_reports'])
        with patch.object(self.app, 'providers', new=providers):
            with patch.object(self.request, 'matchdict', new={'report_key': 'testing_some_random'}):

                # initial view
                response = view.view()
                self.assertEqual(response.status_code, 200)
                # nb. there's a button in there somewhere, but no output title
                self.assertIn("Run Report", response.text)
                self.assertNotIn("Testing Output", response.text)

                # run the report
                with patch.object(self.request, 'GET', new={
                        '__start__': 'start_date:mapping',
                        'date': '2025-01-11',
                        '__end__': 'start_date',
                }):
                    response = view.view()
                    self.assertEqual(response.status_code, 200)
                    # nb. there's a button in there somewhere, *and* an output title
                    self.assertIn("Run Report", response.text)
                    self.assertIn("Testing Output", response.text)

    def test_configure_form(self):
        view = self.make_view()
        providers = dict(self.app.providers)
        providers['wuttatest'] = MagicMock(report_modules=['tests.views.test_reports'])
        with patch.object(self.app, 'providers', new=providers):

            with patch.object(self.request, 'matchdict', new={'report_key': 'testing_some_random'}):
                report = view.get_instance()
                form = view.make_model_form(report)
                self.assertIn('help_text', form.readonly_fields)
                self.assertIn('foo', form)

    def test_normalize_columns(self):
        view = self.make_view()

        columns = view.normalize_columns(['foo'])
        self.assertEqual(columns, [
            {'name': 'foo', 'label': 'foo'},
        ])

        columns = view.normalize_columns([{'name': 'foo'}])
        self.assertEqual(columns, [
            {'name': 'foo', 'label': 'foo'},
        ])

        columns = view.normalize_columns([{'name': 'foo', 'label': "FOO"}])
        self.assertEqual(columns, [
            {'name': 'foo', 'label': 'FOO'},
        ])

        columns = view.normalize_columns([{'name': 'foo', 'label': "FOO", 'numeric': True}])
        self.assertEqual(columns, [
            {'name': 'foo', 'label': 'FOO', 'numeric': True},
        ])

    def test_run_report(self):
        view = self.make_view()
        providers = dict(self.app.providers)
        providers['wuttatest'] = MagicMock(report_modules=['tests.views.test_reports'])
        with patch.object(self.app, 'providers', new=providers):

            with patch.object(self.request, 'matchdict', new={'report_key': 'testing_some_random'}):
                report = view.report_handler.get_report('testing_some_random')
                normal = view.normalize_report(report)
                form = view.make_model_form(normal)

                # typical
                context = view.run_report(report, {'form': form})
                self.assertEqual(sorted(context['report_params']), ['foo', 'start_date'])
                self.assertEqual(context['report_data'], {
                    'output_title': "Testing Output",
                    'data': [{'foo': 'bar'}],
                })
                self.assertIn('report_generated', context)

                # invalid params
                with patch.object(self.request, 'GET', new={'start_date': 'NOT_GOOD'}):
                    context = view.run_report(report, {'form': form})
                    self.assertNotIn('report_params', context)
                    self.assertNotIn('report_data', context)
                    self.assertNotIn('report_generated', context)

                # custom formatter
                with patch.object(report, 'get_output_columns') as get_output_columns:
                    get_output_columns.return_value = [
                        'foo',
                        {'name': 'start_date',
                         'formatter': lambda val: "FORMATTED VALUE"},
                    ]

                    with patch.object(report, 'make_data') as make_data:
                        make_data.return_value = [
                            {'foo': 'bar', 'start_date': datetime.date(2025, 1, 11)},
                        ]

                        context = view.run_report(report, {'form': form})
                        get_output_columns.assert_called_once_with()
                        self.assertEqual(len(context['report_columns']), 2)
                        self.assertEqual(context['report_columns'][0]['name'], 'foo')
                        self.assertEqual(context['report_columns'][1]['name'], 'start_date')
                        self.assertEqual(context['report_data'], {
                            'output_title': "Random Test Report",
                            'data': [{'foo': 'bar', 'start_date': 'FORMATTED VALUE'}],
                        })

    def test_download_data(self):
        view = self.make_view()
        providers = dict(self.app.providers)
        providers['wuttatest'] = MagicMock(report_modules=['tests.views.test_reports'])
        with patch.object(self.app, 'providers', new=providers):
            with patch.object(self.request, 'matchdict', new={'report_key': 'testing_some_random'}):

                params, columns, data = view.get_download_data()
                self.assertEqual(params, {})
                self.assertEqual(columns, [{'name': 'foo', 'label': 'foo'}])
                self.assertEqual(data, {
                    'output_title': "Testing Output",
                    'data': [{'foo': 'bar'}],
                })

    def test_download_path(self):
        view = self.make_view()
        data = {'output_title': "My Report"}
        path = view.get_download_path(data, 'csv')
        self.assertTrue(path.endswith('My Report.csv'))