# -*- coding: UTF-8 -*-

__revision__ = '$Id$'

# Copyright (c) 2006-2011
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 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 Library General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA

# You may use and distribute this software under the terms of the
# GNU General Public License, version 2 or later

import gi
import gettext
import sys
import initialize
import gutils
import gconsole
import config
import os
from time import sleep
import logging
from pathlib import Path
from gi.repository import Gtk
gi.require_version('Gtk', '3.0')

"""
The code within this file is only used to automatically test movie plugins
which support that.
The movie plugin which should be tested has to be added to the
PluginTester.test_plugins list and has to define to classes
SearchPluginTest and PluginTest.
Both classes provide a member called test_configuration which is a
dict in both cases.

SearchPluginTest.test_configuration:
dict { movie_id -> [ expected result count for original url, expected result count for translated url ] }

PluginTest.test_configuration:
dict { movie_id -> dict { attribute -> value } }

value: * True/False if attribute should only be tested for any value or nothing
       * or the expected value
"""

gettext.install('griffith')

logging.basicConfig()
log = logging.getLogger('Griffith')

sys.path.append('plugins/movie')
sys.path.append('plugins/extensions')


def get_plugin_list():
    curr_dir = Path(__file__).absolute().parent
    plugin_dir = curr_dir / 'plugins' / 'movie'
    plugin_list = []
    for plugin in plugin_dir.glob("Plugin*.py"):
        plugin_list.append(plugin.stem)
    return sorted(plugin_list)


def dialog_test_plugin(plugin: str = None):
    """Ask if either all or one plugin should be tested"""
    if plugin:
        msg = f"Test plugin {plugin}?"
    else:
        msg = 'Test all plugins?'
    dialog = Gtk.MessageDialog(
        modal=True, destroy_with_parent=True,
        message_type=Gtk.MessageType.QUESTION, buttons=Gtk.ButtonsType.NONE,
        text=msg
        )
    dialog.add_buttons(Gtk.STOCK_YES, Gtk.ResponseType.YES,
                       Gtk.STOCK_NO, Gtk.ResponseType.NO)
    dialog.set_default_response(Gtk.ResponseType.NO)
    dialog.set_skip_taskbar_hint(False)
    response = dialog.run()
    dialog.destroy()
    return response


class PluginTester:
    """
    test class for movie plugin classes Plugin and SearchPlugin
    it simulates the resolving of movie data for configured movies and
    compares the results with the expected one
    """

    # test_plugins = [
    #     'PluginMovieAllocine',
    #     'PluginMovieAllRovi',
    #     'PluginMovieAmazon',
    #     'PluginMovieAniDB',
    #     'PluginMovieCinematografo',
    #     'PluginMovieCineMovies',
    #     'PluginMovieCineteka',
    #     'PluginMovieClubedevideo',
    #     'PluginMovieCSFD',
    #     'PluginMovieCulturalia',
    #     'PluginMovieDVDEmpire',
    #     'PluginMovieE-Pipoca',
    #     'PluginMovieFilmAffinity',
    #     'PluginMovieFilmeVonAZ',
    #     'PluginMovieFilmtipset',
    #     'PluginMovieHKMDB',
    #     'PluginMovieIMDB',
    #     'PluginMovieIMDB-de',
    #     'PluginMovieIMDB-es',
    #     'PluginMovieKinoDe',
    #     'PluginMovieMovieMeter',
    #     'PluginMovieMyMoviesIt',
    #     'PluginMovieOFDb',
    #     'PluginMovieScope',
    #     'PluginMovieZelluloid',
    #     ]
    test_plugins = get_plugin_list()

    def __init__(self):
        self._tmp_home = None
        self.movie = None
        self.locations = None

    def test_search(self, plugin, logfile, title, cnt_original, cnt_translated):
        """
        Simulates the search for a movie title and compares  the count of
        results with the expected count
        """
        global myconfig
        result = True
        plugin.config = myconfig
        plugin.locations = self.locations
        # plugin.translated_url_search
        plugin.url = plugin.translated_url_search
        if plugin.remove_accents:
            plugin.title = gutils.remove_accents(title, 'utf-8')
        else:
            plugin.title = title
        plugin.search_movies(None)
        plugin.get_searches()
        if not len(plugin.ids) - 1 == cnt_original:    # first entry is always '' (???)
            msg = (f"Title (Translated): {title} - expected: {cnt_original} - "
                   f"found: {len(plugin.ids) - 1}")
            print(msg)
            logfile.write(f"{msg}\n\n")
            for titleFound in plugin.titles:
                logfile.write(titleFound)
                logfile.write('\n')
            logfile.write('\n\n')
            result = False
        # plugin.original_url_search if it is different to
        # plugin.translated_url_search
        if plugin.original_url_search != plugin.translated_url_search:
            plugin.url = plugin.original_url_search
            if plugin.remove_accents:
                plugin.title = gutils.remove_accents(title, 'utf-8')
            else:
                plugin.title = title
            plugin.search_movies(None)
            plugin.get_searches()
            if not len(plugin.ids) - 1 == cnt_translated:    # first entry is always '' (???)
                msg = (f"Title (Original): {title} - expected: {cnt_translated} "
                       f"- found: {len(plugin.ids) - 1}")
                print(msg)
                logfile.write(f"{msg}\n\n")
                for titleFound in plugin.titles:
                    logfile.write(f"{titleFound}\n")
                logfile.write('\n\n')
                result = False
        return result

    #
    # check every configured movie title
    #
    def do_test_search_plugin(self, plugin_name, do_msg_box=True):
        result = True

        try:
            plugin = __import__(plugin_name, globals())
        except ModuleNotFoundError as exc:
            log.info(f"Plugin '{plugin_name}' not found: {exc}")
            return False
        try:
            plugin_test_config = plugin.SearchPluginTest()
        except Exception as exc:
            msg = (f"Warning: SearchPlugin test could not be executed for "
                   f"{plugin_name} because of missing configuration class "
                   f"SearchPluginTest. {type(exc).__name__}")
            print(msg)
            return False

        if plugin_test_config is not None:
            with open(f"{plugin_name}-searchtest.log", 'w+') as log_file:
                for i in plugin_test_config.test_configuration:
                    search_plugin = plugin.SearchPlugin()
                    if not self.test_search(
                            search_plugin, log_file, i,
                            plugin_test_config.test_configuration[i][0],
                            plugin_test_config.test_configuration[i][1]):
                        result = False
                    sleep(1)  # needed for amazon

        if do_msg_box:
            if not result:
                gutils.error('SearchPluginTest %s: Test NOT successful !' %
                             plugin_name)
            else:
                gutils.info('SearchPluginTest %s: Test successful !' %
                            plugin_name)

        return result

    #
    # simulates the resolving of movie data for configured movies and
    # compares the results with the expected once
    #
    def test_one_movie(self, movie_plugin, log_file, results_expected):
        global myconfig
        result = True
        self.movie = movie_plugin
        self.movie.parent_window = None
        self.movie.locations = self.locations
        self.movie.config = myconfig

        fields_to_fetch = [
            'o_title', 'title', 'director', 'plot', 'cast', 'country', 'genre',
            'classification', 'studio', 'o_site', 'site', 'trailer', 'year',
            'notes', 'runtime', 'image', 'rating', 'screenplay', 'cameraman',
            'resolution', 'barcode']

        self.movie.fields_to_fetch = fields_to_fetch

        self.movie.get_movie(None)
        self.movie.parse_movie()

        results = {}
        if 'year' in fields_to_fetch:
            results['year'] = self.movie.year
            fields_to_fetch.pop(fields_to_fetch.index('year'))
        if 'runtime' in fields_to_fetch:
            results['runtime'] = self.movie.runtime
            fields_to_fetch.pop(fields_to_fetch.index('runtime'))
        if 'cast' in fields_to_fetch:
            results['cast'] = gutils.convert_entities(self.movie.cast)
            fields_to_fetch.pop(fields_to_fetch.index('cast'))
        if 'plot' in fields_to_fetch:
            results['plot'] = gutils.convert_entities(self.movie.plot)
            fields_to_fetch.pop(fields_to_fetch.index('plot'))
        if 'notes' in fields_to_fetch:
            results['notes'] = gutils.convert_entities(self.movie.notes)
            fields_to_fetch.pop(fields_to_fetch.index('notes'))
        if 'rating' in fields_to_fetch:
            if self.movie.rating:
                results['rating'] = float(self.movie.rating)
            fields_to_fetch.pop(fields_to_fetch.index('rating'))
        # poster
        if 'image' in fields_to_fetch:
            if self.movie.image:
                results['image'] = self.movie.image
            fields_to_fetch.pop(fields_to_fetch.index('image'))
        # other fields
        for i in fields_to_fetch:
            results[i] = gutils.convert_entities(self.movie[i])

        # check the fields
        for i in results_expected:
            i_val = None
            try:
                i_val = results_expected[i]
                if isinstance(i_val, bool):
                    if i_val:
                        # if not result or i not in results or len(results[i]) < 1:
                        if i not in results or len(results[i]) < 1:
                            msg = (f"Test error: {movie_plugin.movie_id}: Value "
                                   f"expected but nothing returned.\nKey: {i}")
                            print(msg)
                            log_file.write(f"{msg}\n\n")
                            result = False
                    else:
                        if i in results:
                            if isinstance(results[i], int) and results[i] == 0:
                                continue
                            if (not isinstance(results[i], int)
                                    and len(results[i]) < 1):
                                continue
                            msg = (f"Test error: {movie_plugin.movie_id}: No "
                                   f"value expected but something returned.\n"
                                   f"Key: {i}\nValue: {results[i]}")
                            print(msg)
                            log_file.write(f"{msg}\n\n")
                            result = False
                else:
                    if i not in results:
                        msg = (f"Test error: {movie_plugin.movie_id}: Value "
                               f"expected but nothing returned.\nKey: {i}")
                        print(msg)
                        log_file.write(f"{msg}\n\n")
                        result = False
                    else:
                        if not results[i] == i_val:
                            msg = (f"Test error: {movie_plugin.movie_id}: Wrong "
                                   f"value returned.\nKey: {i}\nValue expected:"
                                   f" {i_val}\nValue returned: {results}")
                            print(msg)
                            log_file.write(f"{msg}\n\n")
                            result = False
            except Exception:
                log.exception(f"{i} - {i_val} - {results[i]} - {movie_plugin}")
                result = False
        return result

    def do_test_plugin(self, plugin_name, do_msg_box=True):
        """
        check every configured movie
        """
        result = True

        plugin = __import__(plugin_name)
        try:
            plugin_test_config = plugin.PluginTest()
        except Exception:
            print(f"Warning: Plugin test could not be executed for "
                  f"{plugin_name} because of missing configuration class "
                  "PluginTest.")
            plugin_test_config = None

        if plugin_test_config is not None:
            log_file = open(f"{plugin_name}-loadtest.log", 'w+')
            try:
                for i in plugin_test_config.test_configuration:
                    movie_plugin = plugin.Plugin(i)
                    if not self.test_one_movie(
                            movie_plugin, log_file,
                            plugin_test_config.test_configuration[i]):
                        result = False
                    sleep(1)  # needed for amazon
            finally:
                log_file.close()

        if do_msg_box:
            if not result:
                gutils.error(f"PluginTest {plugin_name}: Test NOT successful !")
            else:
                gutils.info(f"PluginTest {plugin_name}: Test successful !")

        return result

    def do_tests(self, do_msg_box=True):
        """
        main method, iterates through all plugins which should be auto-tested
        and executes the Plugin and SearchPlugin test methods
        """
        global myconfig
        self._tmp_home = None
        home_dir, config_name = gconsole.check_args()
        if not (config_name.find('/') >= 0 or config_name.find('\\') >= 0):
            config_name = os.path.join(home_dir, config_name)
        log.debug("config file used: %s", config_name)
        myconfig = config.Config(file=config_name)
        initialize.locations(self, home_dir)

        gettext.install('griffith', self.locations['i18n'])

        search_successful, search_unsuccessful = [], []
        get_successful, get_unsuccessful = [], []

        test_all = True  # test all plugins ?
        response = dialog_test_plugin()
        if not response == Gtk.ResponseType.YES:
            test_all = False
        # iterate through all testable plugins
        for plugin in self.test_plugins:
            if test_all is False and do_msg_box:
                response = dialog_test_plugin(plugin)
                if not response == Gtk.ResponseType.YES:
                    continue
            print(f"\n___ Starting test of plugin: {plugin}")
            try:
                _plugin = __import__(plugin)
            except ModuleNotFoundError as exc:
                msg = f"Plugin '{plugin}' not found: {exc}"
                log.error(msg)
                print(msg)
                continue
            # search test
            if self.do_test_search_plugin(plugin, False):
                search_successful.append(plugin)
            else:
                search_unsuccessful.append(plugin)
            # movie test
            if self.do_test_plugin(plugin, False):
                get_successful.append(plugin)
            else:
                get_unsuccessful.append(plugin)
        if do_msg_box:
            gutils.info(
                f"SearchPluginTests\n Success: {', '.join(search_successful)}\n"
                f" Failed: {', '.join(search_unsuccessful)}\n\n"
                f"PluginTests\n Success: {', '.join(get_successful)}\n"
                f" Failed: {', '.join(get_unsuccessful)}")


if __name__ == "__main__":
    PluginTester().do_tests()
