# Copyright (C) 2018, 2019, 2020, 2021 The Meme Factory, Inc.
# http://www.karlpinc.com/

# This file is part of PGWUI_Bulk_Upload.
#
# This program is free software: you can redistribute it and/or
# modify it under the terms of the GNU Affero General Public License
# as published by the Free Software Foundation, either version 3 of
# the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public
# License along with this program.  If not, see
# <http://www.gnu.org/licenses/>.
#

# Karl O. Pinc <kop@karlpinc.com>

import pytest
from pgwui_develop import testing

import copy
import logging
from pyramid.threadlocal import get_current_request
from pgwui_common.__init__ import includeme as pgwui_common_includeme
from pgwui_core import constants
from pgwui_bulk_upload.__init__ import includeme as pgwui_bulk_upload_includeme
from pgwui_bulk_upload.views import bulk_upload

# Activiate our pytest plugin
pytest_plugins = ("pgwui",)


# Constants
CHANGED_RESPONSE = {
    'db': 'somedb',
    'db_changed': True,
    'filename': 'file',
    'lines': 5,
    'null_rep': 'NULL',
    'table': 'sometable',
    'trim_upload': constants.CHECKED,
    'upload_null': constants.CHECKED,
    'user': 'someuser',
}

UNCHANGED_RESPONSE = {'db_changed': False}

HOME_PAGE_SETTINGS = {'type': 'URL',
                      'source': '/'}

DEFAULT_URLS = {'pgwui_upload': '/upload',
                'pgwui_logout': '/logout',
                'home_page': '/'}

mock_stats = testing.instance_method_mock_fixture('stats')
mock_quote_columns = testing.instance_method_mock_fixture('quote_columns')


# Helper classes

class MockUploadEngine():
    def __init__(self, run_result):
        self.run_result = run_result

    def run(self):
        return self.run_result


class MockBulkTableUploadHandler():
    def __init__(self, request):
        pass

    def init(self, *args):
        pass

    def stats(self):
        return []

    def quote_columns(self):
        pass


# statmap()


mock_statmap = testing.make_mock_fixture(
    bulk_upload, 'statmap')


# inserted_rows()


mock_inserted_rows = testing.make_mock_fixture(
    bulk_upload, 'inserted_rows')


# relation_stats()


mock_relation_stats = testing.make_mock_fixture(
    bulk_upload, 'relation_stats')


# log_success()

csv_response = copy.deepcopy(CHANGED_RESPONSE)
csv_response['csv_checked'] = constants.CHECKED
tab_response = copy.deepcopy(CHANGED_RESPONSE)
tab_response['csv_checked'] = constants.UNCHECKED


@pytest.mark.parametrize(
    ('response', 'stats', 'i_rows', 'r_stats'), [
        (csv_response, [], 1, []),
        (tab_response,
         [('dirA/foo', 2, 'foo'), ('dirB/foo', 3, 'foo')],
         3,
         [('foo', 5)])])
def test_log_success(caplog, response, stats, i_rows, r_stats):
    '''A log message is output
    '''
    caplog.set_level(logging.DEBUG)

    bulk_upload.log_success(response, stats, i_rows, r_stats)

    log_tuples = caplog.record_tuples
    assert len(log_tuples) == 1
    assert (log_tuples[0][:2]
            == ('pgwui_bulk_upload.views.bulk_upload', logging.INFO))


mock_log_success = testing.make_mock_fixture(
    bulk_upload, 'log_success')


# analyze_results()


@pytest.mark.parametrize(
    ('response', 'log_calls'), [
        (UNCHANGED_RESPONSE, 0),
        (CHANGED_RESPONSE, 1)])
def test_analyze_results(
        response, log_calls,
        mock_log_success, mock_relation_stats, mock_inserted_rows,
        mock_statmap, mock_stats, mock_quote_columns):
    '''Response is returned unchanged, nothing is logged
    '''
    response = copy.deepcopy(response)

    uh = bulk_upload.BulkTableUploadHandler(response)
    mocked_stats = mock_stats(uh)
    mocked_stats.return_value = []
    mocked_quote_columns = mock_quote_columns(uh)

    result = bulk_upload.analyze_results(uh, response)

    assert mock_log_success.call_count == log_calls
    assert mock_relation_stats.call_count == 1
    assert mock_inserted_rows.call_count == 1
    assert mocked_stats.call_count == 1
    assert mocked_quote_columns.call_count == 1

    assert 'stats' in result
    assert 'statmap' in result
    assert 'inserted_rows' in result
    assert 'relation_stats' in result

    del result['stats']
    del result['statmap']
    del result['inserted_rows']
    del result['relation_stats']
    assert result == response


mock_analyze_results = testing.make_mock_fixture(
    bulk_upload, 'analyze_results')


# Fixtures

@pytest.fixture
def isolate_bulk_upload_view(monkeypatch, pyramid_request_config,
                             mock_analyze_results):
    '''Keep bulk_upload_view() from calling anything

    Also, have isolate_bulk_upload_view(response)
    cause UploadEngine to return the supplied "response".
    '''
    def run(response):
        def upload_engine(*args):
            return MockUploadEngine(response)

        monkeypatch.setattr(bulk_upload, 'UploadEngine', upload_engine)
        monkeypatch.setattr(
            bulk_upload, 'BulkTableUploadHandler', MockBulkTableUploadHandler)

        settings = pyramid_request_config.get_settings()
        settings['pgwui'] = settings.get('pgwui', dict())
        settings['pgwui'].update({'home_page': HOME_PAGE_SETTINGS})
        pgwui_common_includeme(pyramid_request_config)
        pgwui_bulk_upload_includeme(pyramid_request_config)
        settings['pgwui'].update({'urls': DEFAULT_URLS})
        pyramid_request_config.add_settings(settings)

        mock_analyze_results.return_value = response

    return run


# Tests

# bulk_upload_view()

def test_bulk_upload_view(isolate_bulk_upload_view):
    '''The response contains and upload handler and
    the request's bulk_upload settings
    '''
    response = copy.deepcopy(UNCHANGED_RESPONSE)
    isolate_bulk_upload_view(response)
    request = get_current_request()
    result = bulk_upload.bulk_upload_view(request)

    bulk_settings = request.registry.settings['pgwui']['pgwui_bulk_upload']
    assert result['pgwui']['upload_settings'] == bulk_settings

    del result['pgwui']  # Remove variables added by pgwui view decorators
    assert result == response
