# -*- coding: utf-8 -*-
"""L3 unit tests: My movies / My series listings (docs/plan/milionar-library/060, 120 §2)."""
import os
import sys
import tempfile
import unittest
from datetime import date

HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, HERE)
import stubs  # noqa: E402

stubs.install()

import xbmcaddon  # noqa: E402
import xbmcplugin  # noqa: E402

from wcs.library2 import fixture_provider, listings, meta, store, urls  # noqa: E402

TODAY = date(2026, 6, 15)


class ListingTests(unittest.TestCase):
    def setUp(self):
        store.set_path(os.path.join(tempfile.mkdtemp(prefix='milionar-db-'), 'library.db'))
        for table in (xbmcplugin.ITEMS, xbmcplugin.ENDED, xbmcplugin.CONTENT, xbmcplugin.SORTS, xbmcplugin.CATEGORY):
            table.clear()
        xbmcaddon.Addon.settings.clear()
        xbmcaddon.Addon.settings['menu_show_ai_chat'] = 'true'
        xbmcaddon.Addon.strings.update({31118: 'Přidat film', 31119: 'plot add movie', 31120: 'Smazat vše filmy', 31121: 'p',
                                        31035: 'Přidat seriál', 31036: 'p', 31037: 'Smazat vše seriály', 31038: 'p', 32290: 'Speciály', 32291: 'vyjde {}', 32292: 'Zatím tu nic není', 32293: 'Sezóna'})
        fixture_provider.seed_meta()
        self._today = listings.date
        listings.date = type('D', (), {'today': staticmethod(lambda: TODAY)})

    def tearDown(self):
        listings.date = self._today

    def items(self, handle=1):
        """Titles only (the buttons on top carry SpecialSort)."""
        return [row for row in xbmcplugin.ITEMS.get(handle, []) if 'SpecialSort' not in row[1].props]

    def buttons(self, handle=1):
        return [row for row in xbmcplugin.ITEMS.get(handle, []) if 'SpecialSort' in row[1].props]

    def add_series(self, tmdb_id=900001):
        store.set_in_list('tv', tmdb_id, True, {'id': tmdb_id, 'name': 'Fixture Show'})

    def run_listing(self, **params):
        listings.run(1, params)

    # ------------------------------------------------------------ movies
    def test_movies_carry_state_and_canonical_urls(self):
        store.set_in_list('movie', 900101, True, {'id': 900101, 'title': 'Fixture Film'})
        store.set_in_list('movie', 900102, True, {'id': 900102, 'title': 'Fixture Film 2'})
        store.save_resume('movie', 900101, 1200.0, 6000.0)
        store.mark_watched('movie', 900102)
        self.run_listing(kind='movies')
        (path1, one, folder1), (path2, two, _f) = self.items()
        self.assertEqual((path1, folder1), (urls.movie_url(900101), False))
        self.assertEqual(one.props['IsPlayable'], 'true')
        self.assertEqual(one.tag.values['ResumePoint'], (1200.0, 6000.0))
        self.assertEqual(one.tag.values['Playcount'], 0)
        self.assertEqual(two.tag.values['Playcount'], 1)
        self.assertEqual((one.props['milionar.type'], one.props['milionar.tmdb']), ('movie', '900101'))
        self.assertEqual(one.tag.values['MediaType'], 'movie')
        self.assertEqual(xbmcplugin.CONTENT[1], 'movies')
        self.assertEqual(xbmcplugin.SORTS[1][0], xbmcplugin.SORT_METHOD_TITLE_IGNORE_THE)
        self.assertEqual(xbmcplugin.ENDED[1], {'succeeded': True, 'updateListing': False, 'cacheToDisc': False})

    def test_movie_uses_full_cached_data_when_present(self):
        store.set_in_list('movie', 900101, True, {'id': 900101, 'title': 'Snapshot'})
        meta.put(meta.key_movie(900101), {'id': 900101, 'title': 'Full', 'release_date': '2020-03-04', 'runtime': 100,
                                          'genres': [{'name': 'Drama'}], 'vote_average': 7.5, 'vote_count': 10,
                                          'credits': {'cast': [{'name': 'A', 'character': 'B'}],
                                                      'crew': [{'name': 'D', 'job': 'Director'}]}}, 10 ** 9)
        self.run_listing(kind='movies')
        tag = self.items()[0][1].tag.values
        self.assertEqual((tag['Title'], tag['Year'], tag['Premiered'], tag['Duration'], tag['Genres']),
                         ('Full', 2020, '2020-03-04', 6000, ['Drama']))
        self.assertEqual((tag['Directors'], tag['Rating']), (['D'], (7.5, 10, 'tmdb', True)))

    def test_empty_lists_show_one_item_that_opens_search(self):
        for kind, content in (('movies', 'movies'), ('series', 'tvshows')):
            xbmcplugin.ITEMS.clear()
            self.run_listing(kind=kind)
            (path, item, folder), = self.items()
            self.assertIn('skin_search', path.replace('%3D', '=') + path)
            self.assertEqual((item.label, folder, xbmcplugin.CONTENT[1]), ('Zatím tu nic není', False, content))

    # ------------------------------------------------------------ series
    def test_series_folder_counts_watched_of_aired(self):
        self.add_series()
        for e in (1, 2):
            store.mark_watched('tv', 900001, 1, e)
        self.run_listing(kind='series')
        (path, item, folder), = self.items()
        self.assertTrue(path.endswith('action=mlist&kind=seasons&tmdb=900001') and folder)
        self.assertEqual(item.tag.values['MediaType'], 'tvshow')
        total = int(item.props['TotalEpisodes'])
        self.assertEqual((int(item.props['WatchedEpisodes']), int(item.props['UnWatchedEpisodes']), item.props['TotalSeasons']),
                         (2, total - 2, '2'))
        self.assertEqual(item.tag.values['Playcount'], 0)

    def test_series_without_cached_data_falls_back_to_the_snapshot(self):
        store.set_in_list('tv', 777, True, {'id': 777, 'name': 'Snapshot Show', 'overview': 'o', 'poster_path': '/p.jpg'})
        self.run_listing(kind='series')
        item = self.items()[0][1]
        self.assertEqual((item.label, item.tag.values['Plot'], item.art['poster']),
                         ('Snapshot Show', 'o', 'https://image.tmdb.org/t/p/w500/p.jpg'))

    def test_seasons_regular_first_then_specials(self):
        self.add_series()
        self.run_listing(kind='seasons', tmdb='900001')
        rows = [(i.props['milionar.s'], i.label) for _p, i, _f in self.items()]
        self.assertEqual(rows, [('-1', '* All seasons'), ('1', 'Sezóna 1'), ('2', 'Sezóna 2'), ('0', 'Speciály')])
        self.assertTrue(all(f for _p, _i, f in self.items()))
        self.assertEqual(xbmcplugin.CONTENT[1], 'seasons')
        self.assertTrue(self.items()[2][0].endswith('action=mlist&kind=episodes&tmdb=900001&s=2'))
        self.assertTrue(self.items()[0][0].endswith('action=mlist&kind=episodes&tmdb=900001&s=all'))

    def test_season_counters_use_aired_episodes_only(self):
        self.add_series()
        store.mark_watched('tv', 900001, 2, 1)
        self.run_listing(kind='seasons', tmdb='900001')
        two = self.items()[2][1]
        everything = self.items()[0][1]
        aired = [e for e in meta.get(meta.key_season(900001, 2))[0]['episodes'] if e['air_date'] <= TODAY.isoformat()]
        self.assertEqual((two.props['TotalEpisodes'], two.props['WatchedEpisodes']), (str(len(aired)), '1'))
        total = sum(int(i.props['TotalEpisodes']) for _p, i, _f in self.items()[1:])
        self.assertEqual((everything.props['TotalEpisodes'], everything.props['WatchedEpisodes']), (str(total), '1'))

    # ------------------------------------------------------------ episodes
    def test_episodes_playable_with_state_and_unaired_shows_date(self):
        self.add_series()
        store.save_resume('tv', 900001, 300.0, 2400.0, 2, 3)
        store.mark_watched('tv', 900001, 2, 1)
        self.run_listing(kind='episodes', tmdb='900001', s='2')
        rows = {i.props['milionar.e']: (p, i) for p, i, _f in self.items()}
        path, first = rows['1']
        self.assertEqual((path, first.tag.values['Playcount']), (urls.episode_url(900001, 2, 1), 1))
        self.assertEqual(rows['3'][1].tag.values['ResumePoint'], (300.0, 2400.0))
        self.assertEqual(first.label.split('.')[0], '01')                  # zero padded like the library
        self.assertNotIn('poster', first.art)                              # so Estuary shows the episode still
        unaired = [(p, i) for p, i in rows.values() if 'mupcoming' in p]
        self.assertTrue(unaired)
        path, item = unaired[0]
        self.assertIn('vyjde', item.label)
        self.assertNotIn('Playcount', item.tag.values)           # no state on an unaired episode
        self.assertEqual(xbmcplugin.SORTS[1], [xbmcplugin.SORT_METHOD_UNSORTED])

    # ------------------------------------------------------------ buttons on top and click action
    def test_top_buttons_of_my_movies_and_series(self):
        store.set_in_list('movie', 900101, True, {'id': 900101, 'title': 'F'})
        self.run_listing(kind='movies')
        paths = [(i.label, p, f) for p, i, f in self.buttons()]
        self.assertEqual([b[0] for b in paths], ['Poradit se s AI (beta)', 'Přidat film', '[COLOR red]Smazat vše filmy[/COLOR]'])
        self.assertTrue(paths[0][1].endswith('action=ai_chat_recommendation&context_type=my_movies&media_type=movie') and paths[0][2])
        self.assertTrue(paths[1][1].endswith('action=tmdb_add_movie&new=1&add_to_my_movies=1') and not paths[1][2])
        self.assertTrue(paths[2][1].endswith('action=tmdb_remove_all_movies'))
        self.assertEqual([i.props['SpecialSort'] for _p, i, _f in self.buttons()], ['top'] * 3)
        xbmcplugin.ITEMS.clear()
        self.add_series()
        self.run_listing(kind='series')
        self.assertEqual([p.split('action=')[1] for p, _i, _f in self.buttons()],
                         ['ai_chat_recommendation&context_type=my_series&media_type=tv',
                          'tmdb_add_series&new=1&add_to_my_series=1', 'tmdb_remove_all_series'])

    def test_empty_list_has_no_delete_all_and_ai_follows_its_setting(self):
        xbmcaddon.Addon.settings['menu_show_ai_chat'] = 'false'
        self.run_listing(kind='movies')
        self.assertEqual([i.label for _p, i, _f in self.buttons()], ['Přidat film'])
        self.assertEqual(len(self.items()), 1)                       # the "nothing here yet" item

    def test_click_follows_the_default_movie_action_setting(self):
        store.set_in_list('movie', 900101, True, {'id': 900101, 'title': 'F'})
        self.run_listing(kind='movies')
        path, item, _f = self.items()[0]
        self.assertEqual((path, item.props.get('IsPlayable')), (urls.movie_url(900101), 'true'))
        xbmcplugin.ITEMS.clear()
        xbmcaddon.Addon.settings['default_movie_action'] = '0'       # "Zobrazit detaily"
        self.run_listing(kind='movies')
        path, item, _f = self.items()[0]
        self.assertTrue(path.endswith('action=show_wcs_details&media_type=movie&tmdb_id=900101'))
        self.assertNotIn('IsPlayable', item.props)
        self.assertEqual(item.tag.values['MediaType'], 'movie')       # state props stay

    def test_all_seasons_lists_every_episode_with_season_prefix(self):
        self.add_series()
        self.run_listing(kind='episodes', tmdb='900001', s='all')
        labels = [i.label for _p, i, _f in self.items()]
        self.assertTrue(labels[0].startswith('1x01. '))
        self.assertTrue(any(l.startswith('2x01. ') for l in labels))
        self.assertEqual(self.items()[0][1].props['milionar.s'], '1')

    def test_upcoming_click_resolves_to_nothing_and_notifies(self):
        import xbmcgui
        listings.upcoming(5, {'text': 'S02E07 vyjde 22. 6.'})
        self.assertEqual(xbmcplugin.RESOLVED[-1], (5, False))
        self.assertEqual(xbmcgui.NOTIFICATIONS[-1], ('Milionář', 'S02E07 vyjde 22. 6.'))

    def test_a_failing_listing_closes_the_directory(self):
        original = listings.movies
        listings.movies = lambda handle: 1 / 0
        try:
            self.run_listing(kind='movies')
        finally:
            listings.movies = original
        self.assertEqual(xbmcplugin.ENDED[1]['succeeded'], False)


if __name__ == '__main__':
    unittest.main()
