# -*- coding: utf-8 -*-
"""Mirror of the addon's watch state into Kodi, and adoption of changes made in Kodi (040 §6)."""
import datetime
import json

import xbmc

from wcs.library2 import policy, store, urls


def _rpc(method, params):
    return json.loads(xbmc.executeJSONRPC(json.dumps({'jsonrpc': '2.0', 'id': 1, 'method': method, 'params': params})))


def _kodi_time(epoch):
    return datetime.datetime.fromtimestamp(epoch).strftime('%Y-%m-%d %H:%M:%S') if epoch else ''


def _row(media_type, tmdb_id, season, episode):
    return store.get_state('movie' if media_type == 'movie' else 'tv', tmdb_id, season, episode)


def canonical_for(media_type, tmdb_id, season=None, episode=None):
    return urls.movie_url(tmdb_id) if media_type == 'movie' else urls.episode_url(tmdb_id, season, episode)


def push(media_type, tmdb_id, season=None, episode=None):
    """Write the stored state of one item into Kodi's record for its canonical URL (Files.SetFileDetails)."""
    row = _row(media_type, tmdb_id, season, episode)
    if not row:
        return False
    params = {'file': canonical_for(media_type, tmdb_id, season, episode), 'media': 'video',
              'playcount': int(row['playcount'] or 0), 'lastplayed': _kodi_time(row['last_played_at']),
              'resume': {'position': float(row['resume_pos'] or 0), 'total': float(row['resume_total'] or 0)}}
    ok = _rpc('Files.SetFileDetails', params).get('result') == 'OK'
    if ok:
        return store.set_mirrored('movie' if media_type == 'movie' else 'tv', tmdb_id,
                                  row['playcount'], row['resume_pos'], season, episode,
                                  version=row['state_version'])
    return ok


def read_kodi(media_type, tmdb_id, season=None, episode=None):
    """Kodi's current {'playcount', 'resume_pos', 'resume_total'} for the item, or None."""
    reply = _rpc('Files.GetFileDetails', {'file': canonical_for(media_type, tmdb_id, season, episode),
                                          'media': 'video', 'properties': ['playcount', 'resume']})
    details = (reply.get('result') or {}).get('filedetails')
    if not details:
        return None
    resume = details.get('resume') or {}
    return {'playcount': int(details.get('playcount') or 0), 'resume_pos': float(resume.get('position') or 0),
            'resume_total': float(resume.get('total') or 0)}


def check(media_type, tmdb_id, season=None, episode=None):
    """Adopt a Kodi change or queue our newer state; never overwrite a pending local intent."""
    from wcs.library2 import jobs
    kind = 'movie' if media_type == 'movie' else 'tv'
    if store.mirror_pending(kind, tmdb_id, season, episode) or jobs.has_pending(kind, tmdb_id):
        return None
    row = _row(media_type, tmdb_id, season, episode)
    if not row or row.get('mirrored_at') is None:
        return None
    kodi = read_kodi(media_type, tmdb_id, season, episode)
    if kodi is None:
        return None
    mirrored = {'playcount': row.get('mirrored_playcount'), 'resume_pos': row.get('mirrored_resume_pos'),
                'at': row.get('mirrored_at')}
    decision = policy.reconcile({'last_played_at': row.get('last_played_at')}, kodi, mirrored)
    differs = (kodi['playcount'] != int(row.get('mirrored_playcount') or 0)
               or abs(kodi['resume_pos'] - float(row.get('mirrored_resume_pos') or 0)) > policy.RESUME_TOLERANCE_S)
    if decision is not None or differs:
        table, where, args, _cols, _vals = store._state_keys(kind, tmdb_id, season, episode)
        with store.tx() as conn:
            current = conn.execute('SELECT state_version FROM {} WHERE {}'.format(table, where), args).fetchone()
            pending = conn.execute('SELECT 1 FROM mirror_pending WHERE media_type=? AND tmdb_id=? AND season=? AND episode=?',
                                   (kind, int(tmdb_id), season if season is not None else -1,
                                    episode if episode is not None else -1)).fetchone()
            marks = conn.execute('SELECT 1 FROM pending_marks WHERE media_type=? AND tmdb_id=? LIMIT 1',
                                 (kind, int(tmdb_id))).fetchone()
            if not current or current['state_version'] != row['state_version'] or pending or marks:
                return None
            if decision is not None:
                total = kodi['resume_total'] if decision['resume_pos'] > 0 else 0
                conn.execute('UPDATE {} SET playcount=?,resume_pos=?,resume_total=? WHERE {}'.format(table, where),
                             (decision['playcount'], decision['resume_pos'], total) + args)
                store._touch(conn, kind, tmdb_id, store._now())
            store._queue_mirror(conn, kind, tmdb_id, season, episode)
        return 'adopted' if decision is not None else 'remirrored'
    return None


def check_paths(paths):
    """check() for a list of canonical URLs (the items a Videos window or Home row shows)."""
    changed = 0
    for path in paths:
        info = urls.parse(path)
        if not info:
            continue
        if info['type'] == 'movie':
            result = check('movie', info['tmdb'])
        else:
            result = check('episode', info['tmdb'], info['s'], info['e'])
        if result == 'adopted':
            changed += 1
    return changed
