"""Targeted offline freshness, coherent snapshots and bounded provider attempts."""
import os
import sys
import tempfile
import unittest

sys.path.insert(0, os.path.dirname(__file__))
import stubs
stubs.install()

from wcs.library2 import meta, refresher, store, tmdb
from wcs.skin import refresh


class MetadataRefreshTests(unittest.TestCase):
    def setUp(self):
        directory = tempfile.TemporaryDirectory(prefix='metadata-refresh-')
        self.addCleanup(directory.cleanup)
        old = store._override_path
        store.set_path(os.path.join(directory.name, 'library.db'))
        self.addCleanup(store.set_path, old)

    def seed(self):
        show = {'id': 1, 'name': 'Old', 'seasons': [{'season_number': 1, 'episode_count': 1}]}
        season = {'episodes': [{'episode_number': 1, 'air_date': '2020-01-01'}]}
        meta.put_many([('tv:1', show, 100), ('season:1:1', season, 100)], now=0)
        return show, season

    def test_identical_metadata_renews_age_without_refreshing_rows(self):
        old, _season = self.seed()
        before = refresh.revisions(refresh.ALL_ROWS)
        meta.put('tv:1', dict(reversed(list(old.items()))), 100, now=200)
        self.assertEqual(refresh.revisions(refresh.ALL_ROWS), before)
        self.assertFalse(meta.get('tv:1', now=250)[1])

    def test_generic_http_cache_does_not_refresh_home(self):
        before = refresh.revisions(refresh.ALL_ROWS)
        meta.put('http:synthetic', {'value': 1}, 100)
        self.assertEqual(refresh.revisions(refresh.ALL_ROWS), before)

    def test_failed_season_keeps_entire_previous_generation(self):
        old, season = self.seed()
        before = refresh.revisions(refresh.ALL_ROWS)
        def fetch(path, params):
            return dict(old, name='New') if '/season/' not in path else None
        with self.assertRaises(RuntimeError):
            tmdb.refresh_show(1, fetch)
        self.assertEqual(meta.get('tv:1')[0], old)
        self.assertEqual(meta.get('season:1:1')[0], season)
        self.assertEqual(refresh.revisions(refresh.ALL_ROWS), before)

    def test_incomplete_episode_count_keeps_generation(self):
        old, _season = self.seed()
        def fetch(path, params):
            return dict(old, name='New') if '/season/' not in path else {'episodes': []}
        with self.assertRaises(RuntimeError):
            tmdb.refresh_show(1, fetch)
        self.assertEqual(meta.get('tv:1')[0], old)

    def test_coherent_generation_bumps_each_dependent_row_once(self):
        old, season = self.seed()
        before = refresh.revisions(refresh.ALL_ROWS)
        def fetch(path, params):
            return dict(old, name='New') if '/season/' not in path else dict(season, name='New season')
        self.assertEqual(tmdb.refresh_show(1, fetch), ['tv:1', 'season:1:1'])
        after = refresh.revisions(refresh.ALL_ROWS)
        for row in refresh.media_rows('tv', True) | {'widget_mytv_next'}:
            self.assertEqual(after[row], before[row] + 1)
        self.assertEqual(after['widget_tmdb_popular_movies'], before['widget_tmdb_popular_movies'])

    def test_missing_season_is_due_even_with_fresh_show(self):
        old, _season = self.seed()
        store.set_in_list('tv', 1, True, {'id': 1})
        meta.put('tv:1', old, 1000, now=500)
        meta.delete('season:1:1')
        self.assertEqual(refresher.due_series(now=600), [1])

    def test_failed_attempts_are_bounded_and_next_batch_is_not_starved(self):
        for ident in range(1, 9):
            store.set_in_list('tv', ident, True, {'id': ident})
        called = []
        def fetch(path, params):
            called.append(path)
            raise OSError('synthetic unavailable provider')
        self.assertEqual(refresher.refresh_due(fetch=fetch, playing=lambda: False, limit=3), [])
        self.assertEqual(called, ['tv/1', 'tv/2', 'tv/3'])
        called.clear()
        refresher.refresh_due(fetch=fetch, playing=lambda: False, limit=3)
        self.assertEqual(called, ['tv/4', 'tv/5', 'tv/6'])

    def test_bulk_status_keeps_stale_data_and_omits_missing(self):
        old, _season = self.seed()
        self.assertEqual(meta.get_many_status(['tv:1', 'tv:99'], now=200), {'tv:1': (old, True)})

    def test_movie_refresh_rejects_failure_and_preserves_cache(self):
        meta.put('movie:7', {'id': 7, 'title': 'Old', 'genres': []}, 100)
        with self.assertRaises(RuntimeError):
            tmdb.refresh_movie(7, lambda path, params: None)
        self.assertEqual(meta.get('movie:7')[0]['title'], 'Old')


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