# -*- coding: utf-8 -*-
"""Next episode / Continue logic (docs/plan/milionar-library/050 §1-2, §5): pure functions, no Kodi, no store.

Inputs are plain dicts: TMDb show + season data (as cached in `meta`), watch states keyed by (season, episode).
"""
from datetime import date

ENDED_STATUSES = ('Ended', 'Canceled', 'Cancelled')


def _parse_date(text):
    try:
        year, month, day = (int(x) for x in str(text)[:10].split('-'))
        return date(year, month, day)
    except (ValueError, TypeError):
        return None


def episode_order(show, seasons, today):
    """Regular episodes of a series in play order (specials, season 0, excluded - decision 9).

    show     TMDb tv details ('seasons': [{'season_number'}...])
    seasons  {season_number: TMDb season details ('episodes': [...])}; seasons without data are skipped
    Returns [{'s', 'e', 'air_date', 'aired', 'name', 'runtime', 'still'}...]. An episode without air_date counts as
    not aired.
    """
    numbers = sorted({int(s['season_number']) for s in (show.get('seasons') or []) if int(s.get('season_number', 0)) > 0})
    order = []
    for number in numbers:
        data = seasons.get(number) or {}
        for ep in sorted(data.get('episodes') or [], key=lambda e: e.get('episode_number', 0)):
            air = _parse_date(ep.get('air_date'))
            order.append({'s': number, 'e': int(ep['episode_number']), 'air_date': air.isoformat() if air else '',
                          'aired': bool(air and air <= today), 'name': ep.get('name', ''),
                          'runtime': ep.get('runtime') or 0, 'still': ep.get('still_path') or ''})
    return order


def _state(states, ep):
    return states.get((ep['s'], ep['e'])) or {}


def series_watched(order, states):
    """True when every aired regular episode was watched (playcount > 0); an empty order is not watched."""
    aired = [ep for ep in order if ep['aired']]
    return bool(aired) and all(int(_state(states, ep).get('playcount') or 0) > 0 for ep in aired)


def next_up(order, states, status, today=None):
    """Continue state of a series (050 §2).

    Returns {'kind': 'resume' | 'next' | 'upcoming' | 'finished' | 'caught_up' | None, 'episode': dict | None}
      resume     an episode with a resume point (newest play first)
      next       first episode after the latest watched one *by order*
      upcoming   that next episode exists but is not aired (not playable, shows its air date)
      finished   nothing follows and the series ended
      caught_up  nothing follows, the series is still running (nothing announced yet)
      None       no watch activity at all (not in Continue)
    """
    active = [(ep, _state(states, ep)) for ep in order]
    in_progress = [(ep, st) for ep, st in active if float(st.get('resume_pos') or 0) > 0 and int(st.get('playcount') or 0) == 0]
    if in_progress:
        ep = max(in_progress, key=lambda pair: pair[1].get('last_played_at') or 0)[0]
        return {'kind': 'resume', 'episode': ep}
    watched_indexes = [i for i, (_ep, st) in enumerate(active) if int(st.get('playcount') or 0) > 0]
    if not watched_indexes:
        return {'kind': None, 'episode': None}
    return after_index(order, max(watched_indexes), status)


def after_index(order, index, status):
    """The state of the series once the episode at `index` in `order` is watched (also used for autoplay)."""
    if index + 1 < len(order):
        candidate = order[index + 1]
        return {'kind': 'next' if candidate['aired'] else 'upcoming', 'episode': candidate}
    return {'kind': 'finished' if status in ENDED_STATUSES else 'caught_up', 'episode': None}


def next_after(order, season, episode, status):
    """Autoplay: what follows S<season>E<episode> as if it were watched, rollover included."""
    for i, ep in enumerate(order):
        if (ep['s'], ep['e']) == (season, episode):
            return after_index(order, i, status)
    # a special or an episode TMDb no longer lists: no automatic follow-up
    return {'kind': 'caught_up', 'episode': None}


def next_in_collection(collection, current_id, movie_states, today):
    """Next film of the collection by release date that is released and not watched (050 §5), or None.

    collection   TMDb collection ('parts': [{'id', 'title', 'release_date'}])
    movie_states {tmdb_id: {'playcount'}}
    """
    parts = []
    for part in collection.get('parts') or []:
        released = _parse_date(part.get('release_date'))
        if released and released <= today and part.get('id') != current_id:
            parts.append((released, part))
    for _released, part in sorted(parts, key=lambda pair: (pair[0], pair[1].get('id', 0))):
        if int((movie_states.get(part['id']) or {}).get('playcount') or 0) == 0:
            return part
    return None


def air_label(iso_date):
    """'2026-10-12' -> '12. 10.' (Czech short date used in "vyjde 12. 10.")."""
    parsed = _parse_date(iso_date)
    return '{}. {}.'.format(parsed.day, parsed.month) if parsed else ''
