# -*- coding: utf-8 -*-
"""Pro tebe harness: builds the real snapshot outside Kodi on a copy of the live library.db (docs/plan/pro-tebe/010 §8).

    python3 scripts/foryou_harness/foryou_harness.py build [--hour 20] [--mode all|movie|tv] [--sync]
    python3 scripts/foryou_harness/foryou_harness.py seeds
    python3 scripts/foryou_harness/foryou_harness.py explain movie:603
    python3 scripts/foryou_harness/foryou_harness.py like movie:27205      # feedback into the copy (like/dislike/hide/pick/clear)

The copy lives in work/ (gitignored) and keeps its TMDb cache between runs; --sync takes a fresh copy of the live
database (the live one is only read, through SQLite's backup API). Every build writes out/snapshot_<time>.json.
"""
import argparse
import json
import os
import sqlite3
import sys
import time

HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, os.path.join(os.path.dirname(HERE), 'search_harness'))
import kodi_stubs  # noqa: E402

kodi_stubs.install()

from wcs.foryou import api, engine  # noqa: E402
from wcs.library2 import store  # noqa: E402

LIVE_DB = os.path.join(kodi_stubs.KODI_HOME, 'userdata/addon_data/plugin.video.milionar/library.db')
WORK = os.path.join(HERE, 'work')
OUT = os.path.join(HERE, 'out')
WORK_DB = os.path.join(WORK, 'library.db')

REASONS = {'because_watched': 'Protože jsi viděl {}', 'because_liked': 'Podobné jako {}',
           'because_list': 'Podobné jako {} z tvého seznamu', 'maker': 'Od tvůrce {}', 'actor': 'Hraje {}',
           'collection': 'Z kolekce {}', 'genre': 'Tvůj žánr: {}', 'trending': 'Teď frčí',
           'quality': 'Vysoce hodnocené', 'similar_to': 'Podobné jako {}'}
ROWS = {'for_you': 'Pro tebe', 'top10': 'Top 10 v ČR', 'because': 'Protože jsi viděl {}', 'new': 'Nově s CZ dabingem',
        'gems': 'Skryté klenoty'}
SLOTS = {'morning': 'Na ráno', 'afternoon': 'Na odpoledne', 'evening': 'Na dnešní večer', 'night': 'Na noc'}


def sync(force=False, empty=False, live=False):
    os.makedirs(WORK, exist_ok=True)
    if live:                                # the real library.db of Kodi (what the service does): read + write meta
        store.set_path(LIVE_DB)
        return
    if empty:                               # a household with no history (cold start); TMDb cache starts empty too
        path = os.path.join(WORK, 'empty.db')
        if os.path.exists(path):
            os.remove(path)
        store.set_path(path)
        return
    if force or not os.path.exists(WORK_DB):
        if os.path.exists(WORK_DB):
            os.remove(WORK_DB)
        source = sqlite3.connect('file:{}?mode=ro'.format(LIVE_DB), uri=True)
        target = sqlite3.connect(WORK_DB)
        source.backup(target)
        source.close()
        target.close()
        print('work copy <- live library.db')
    store.set_path(WORK_DB)


def reason_text(snapshot, reason):
    arg = reason.get('arg') or snapshot['seed_titles'].get(reason.get('seed', ''), '')
    if reason['kind'] == 'genre':
        arg = snapshot['genres'].get(str(arg)) or snapshot['genres'].get(arg) or arg
    return REASONS.get(reason['kind'], reason['kind']).format(arg)


def row_title(snapshot, row):
    if row['kind'] == 'because':
        template = 'Podobné jako {}' if row['reason']['kind'] == 'because_liked' else ROWS['because']
        return template.format(snapshot['seed_titles'].get(row['seed'], row['seed']))
    if row['kind'] == 'tonight':
        return SLOTS[row['slot']]
    return ROWS[row['kind']]


def print_snapshot(snapshot, mode, per_row):
    print('\n=== Pro tebe [{}] slot={} pool={} {}s tmdb={}'.format(
        mode, snapshot['slot'], snapshot['stats']['pool'], snapshot['stats']['seconds'], snapshot['stats']['tmdb']))
    print('taste genres:', ', '.join(snapshot['genres'].get(str(g)) or snapshot['genres'].get(g) or str(g)
                                     for g in snapshot['taste']))
    for row in snapshot['modes'][mode]:
        print('\n## {}  ({})'.format(row_title(snapshot, row), len(row['items'])))
        for n, key in enumerate(row['items'][:per_row]):
            it = snapshot['items'][key]
            rt = '{}m'.format(it['runtime']) if it['runtime'] else ''
            print('  {:2d}. {:3d}%  {:<38.38} {} {:<2} {:4} {:>4} ★{:.1f} {:6} {:5} | {}'.format(
                n + 1, it['match'], it['title'], it['year'] or '----', 'tv' if it['media'] == 'tv' else 'mv',
                it['language'], rt, it['rating'], it.get('lang', ''), it.get('quality', ''),
                reason_text(snapshot, row.get('reason') or it['reason'])))


def cmd_build(args):
    sync(args.sync, args.empty, args.live)
    snapshot = engine.build(hour=args.hour, log=lambda m: print('..', m), verify=not args.no_verify)
    snapshot = json.loads(json.dumps(snapshot, default=list))       # what the store sees (genre keys become str)
    os.makedirs(OUT, exist_ok=True)
    path = os.path.join(OUT, 'snapshot_{}.json'.format(time.strftime('%Y%m%d_%H%M%S')))
    with open(path, 'w', encoding='utf-8') as handle:
        json.dump(snapshot, handle, ensure_ascii=False, indent=1)
    for mode in ([args.mode] if args.mode else ['all']):
        print_snapshot(snapshot, mode, args.per_row)
    print('\nsnapshot:', os.path.relpath(path))
    if args.save:
        engine.save(snapshot)
        print('saved into the work copy (meta {})'.format(engine.SNAPSHOT_KEY))


def cmd_seeds(args):
    sync(args.sync, live=args.live)
    from wcs.foryou import signals
    seeds, exclude = signals.load()
    for s in seeds:
        data = api.detail(s['media'], s['id']) or {}
        print('{:+.2f}  {:<8} {}:{:<8} {}'.format(s['weight'], s['kind'], s['media'], s['id'],
                                                 data.get('title') or data.get('name') or ''))
    print('excluded:', len(exclude))


def cmd_explain(args):
    files = sorted(f for f in os.listdir(OUT) if f.startswith('snapshot_'))
    snapshot = json.load(open(os.path.join(OUT, files[-1]), encoding='utf-8'))
    it = snapshot['items'].get(args.key)
    if not it:
        print('not on the page:', args.key)
        return
    print(json.dumps({k: it[k] for k in ('title', 'year', 'match', 'score', 'parts', 'kinds', 'reason', 'runtime',
                                         'rating', 'votes', 'language')}, ensure_ascii=False, indent=1))
    print('reason:', reason_text(snapshot, it['reason']))


def cmd_feedback(args, value):
    sync(args.sync, live=args.live)
    media, tmdb_id = args.key.split(':')
    store.set_feedback(media, int(tmdb_id), None if value == 'clear' else value)
    print('feedback', value, args.key)


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('command', choices=['build', 'seeds', 'explain', 'like', 'dislike', 'hide', 'pick', 'clear'])
    parser.add_argument('key', nargs='?')
    parser.add_argument('--hour', type=int)
    parser.add_argument('--mode', choices=['all', 'movie', 'tv'])
    parser.add_argument('--per-row', type=int, default=10)
    parser.add_argument('--sync', action='store_true')
    parser.add_argument('--save', action='store_true')
    parser.add_argument('--empty', action='store_true', help='cold start: an empty library')
    parser.add_argument('--no-verify', action='store_true', help='skip the Webshare check (P1 behaviour)')
    parser.add_argument('--live', action='store_true', help='use the live library.db (with --save: publish to Kodi)')
    args = parser.parse_args()
    if args.command == 'build':
        cmd_build(args)
    elif args.command == 'seeds':
        cmd_seeds(args)
    elif args.command == 'explain':
        cmd_explain(args)
    else:
        cmd_feedback(args, args.command)
    for level, msg in kodi_stubs.LOG:
        if level >= 2 and 'ProTebe' in msg:
            print('LOG', msg)


if __name__ == '__main__':
    main()
