#!/usr/bin/env python3
"""Собирает реестр отчётов платформы: local/platform/data/reports.json.
Источники (все - копии halleg в local/Source и разовая выгрузка статистики в local/designs/night/index.html):
 - META в sidebar.js: 16 слагов навигации и их группы;
 - блок LINKS «Прямые ссылки» в макете night: 18 слагов;
 - REPORTS в макете night: посещения и пользователи за 7 дней (разовая выгрузка, снимок ~31.08-06.09.2026);
 - сохранённые копии страниц: слаг из «saved from url», эндпоинты /api/* из html и js.
Названия uk/ru - НАШ перевод, в источнике названия отчётов не переводятся (sidebar.js: «report names from /api/me are kept as-is»)."""
import re, json, glob, os
ROOT = os.path.join(os.path.dirname(__file__), '..')
SRC = os.path.join(ROOT, 'Source')
OUT = os.path.join(ROOT, 'platform', 'data', 'reports.json')

# --- sidebar.js META: slug -> group
sb = open(glob.glob(os.path.join(SRC, '*_files', 'sidebar.js'))[0], encoding='utf-8').read()
meta_src = re.search(r'var META=\{(.*?)\};', sb, re.S).group(1)
NAV = {m.group(1): m.group(2) for m in re.finditer(r'"([a-z0-9-]+)":\{grp:"(\w+)"', meta_src)}

# --- макет night: LINKS и REPORTS
night = open(os.path.join(ROOT, 'designs', 'night', 'index.html'), encoding='utf-8').read()
links_src = re.search(r'const LINKS = \[(.*?)\n\];', night, re.S).group(1)
LINKS = {}
for gm in re.finditer(r"\['([^']+)', \[(.*?)\]\](?=,\s*\n|\s*$)", links_src, re.S):
    for lm in re.finditer(r"\['([^']+)','([a-z0-9-]+)'\]", gm.group(2)):
        LINKS[lm.group(2)] = dict(name=lm.group(1), group=gm.group(1))
rep_src = re.search(r'const REPORTS = \[(.*?)\n\]', night, re.S).group(1)
STATS = {}
for m in re.finditer(r"\['([^']+)', (\d+), (\d+), (true|false), (true|false), '((?:[^'\\]|\\.)*)'\]", rep_src):
    STATS[m.group(1)] = dict(visits_7d=int(m.group(2)), users_7d=int(m.group(3)), is_new=m.group(5) == 'true', note=m.group(6).replace("\\'", "'"))
# имена в статистике -> слаги (сопоставление руками, имена в выгрузке сокращённые)
STAT2SLUG = {'Trader Operational': 'trader-operational', 'Trader Shifts': 'trader-shifts', 'Weekly KPI': 'trader-weekly',
             'Trader Scoreboard': 'trader-scoreboard', 'Trader Detailed': 'trader-detailed', 'Trader Affiliate': 'trader-affiliate',
             'Retention & Churn': 'churn', 'Affiliate Native': 'trader-affiliate-native', 'Trader KPI': 'trader-kpi',
             'Hourly Calculator': 'trader-hourly-calculator', 'Affiliate Weekly': 'trader-affiliate-weekly',
             'Trader Teamlead': 'trader-teamlead', 'Merchant OUT/IN': 'merchant-out-in'}

# --- копии страниц: слаг, заголовок, эндпоинты
COPIES = {}
for page in glob.glob(os.path.join(SRC, '*.html')):
    s = open(page, encoding='utf-8', errors='ignore').read()
    slug = re.search(r'saved from url=\(\d+\)https?://[^/]+/([a-z0-9-]+)', s[:600]).group(1)
    title = re.search(r'<title>([^<]*)', s).group(1).replace('&amp;', '&')
    texts = [s] + [open(js, encoding='utf-8', errors='ignore').read() for js in glob.glob(page[:-5].replace('_', ' ') + '_files/*.js')]
    eps = set()
    for t in texts:
        for m in re.finditer(r"""['"`](/api/[A-Za-z0-9_/.-]+)""", t):
            ep = m.group(1)
            if ep in ('/api/me', '/api/filters', '/api/logout') or ep.startswith('/api/me?'): continue
            eps.add(ep)
    saved = re.search(r'saved from url', s[:600]) is not None
    COPIES[slug] = dict(file=os.path.basename(page), title=title, endpoints=sorted(eps), size_bytes=os.path.getsize(page))

# Названия отчётов: en из источника (title страницы или LINKS), uk/ru - наш перевод
NAMES = {
 'trader-operational': ('Trader Operational Dashboard', 'Операційний дашборд трейдерів', 'Операционный дашборд трейдеров'),
 'trader-shifts': ('Trader Shifts', 'Зміни трейдерів', 'Смены трейдеров'),
 'trader-weekly': ('Weekly KPI Dashboard', 'Тижневий KPI дашборд', 'Недельный KPI дашборд'),
 'trader-scoreboard': ('Trader Scoreboard', 'Рейтинг трейдерів', 'Рейтинг трейдеров'),
 'trader-detailed': ('Trader Detailed Dashboard', 'Детальний дашборд трейдера', 'Детальный дашборд трейдера'),
 'trader-kpi': ('Trader KPI Dashboard', 'KPI дашборд трейдерів', 'KPI дашборд трейдеров'),
 'trader-hourly-calculator': ('Trader Hourly Calculator', 'Погодинний калькулятор трейдерів', 'Почасовой калькулятор трейдеров'),
 'trader-capacity': ('Trader Capacity', 'Ємність трейдерів', 'Ёмкость трейдеров'),
 'traders-active': ('Active Traders', 'Активні трейдери', 'Активные трейдеры'),
 'trader-teamlead': ('Trader Teamlead', 'Дашборд тимліда', 'Дашборд тимлида'),
 'trader-merchant-slu': ('Merchant SLU', 'Мерчанти та SLU', 'Мерчанты и SLU'),
 'merchant-out-in': ('Merchant OUT/IN', 'Мерчанти OUT/IN', 'Мерчанты OUT/IN'),
 'trader-affiliate': ('Affiliate Dashboard', 'Дашборд афіліатів', 'Дашборд аффилиатов'),
 'trader-affiliate-weekly': ('Affiliate Weekly Report', 'Тижневий звіт по афіліатах', 'Недельный отчёт по аффилиатам'),
 'trader-affiliate-tier': ('Affiliate Tier Board', 'Тiри афіліатів', 'Тиры аффилиатов'),
 'trader-affiliate-native': ('Affiliate Native', 'Affiliate Native', 'Affiliate Native'),
 'churn': ('Retention & Churn', 'Утримання та відтік', 'Удержание и отток'),
 'threshold-analysis': ('Threshold Analysis', 'Аналіз порогів', 'Анализ порогов'),
 'metrics-legend': ('Metrics Legend', 'Легенда метрик', 'Легенда метрик'),
}
GROUP_UI = {'Trader': 'Trader', 'Merchant': 'Merchant', 'Affiliate': 'Partners', 'Partners': 'Partners', 'Retention': 'Retention', 'Other': 'Other', 'Аналитика / справка': 'Other'}
# группа halleg -> отдел платформы (стартовое предположение: всё текущее - отдел аналитики, кроме афилиатских, которые
# относятся к отделу аффилиатов; подтверждения нет)
DEPT = {'Trader': 'analytics', 'Merchant': 'analytics', 'Partners': 'affiliates', 'Retention': 'analytics', 'Other': 'analytics'}

slugs = sorted(set(NAV) | set(LINKS) | set(COPIES) | set(STAT2SLUG.values()))
reports = []
for slug in slugs:
    grp_raw = NAV.get(slug) or LINKS.get(slug, {}).get('group') or 'Other'
    grp = GROUP_UI.get(grp_raw, 'Other')
    st = next((v for k, v in STATS.items() if STAT2SLUG.get(k) == slug), None)
    en, uk, ru = NAMES[slug]
    r = dict(
        slug=slug,
        name={'en': en, 'uk': uk, 'ru': ru},
        name_source='halleg title' if slug in COPIES else ('halleg direct links' if slug in LINKS else 'halleg sidebar META'),
        group=grp,
        department=DEPT[grp],
        kind='reference' if slug in ('metrics-legend',) else 'report',
        admin_only=slug == 'threshold-analysis',
        listed_in={'sidebar': slug in NAV, 'direct_links': slug in LINKS, 'usage_stats': st is not None},
        usage_7d=(dict(visits=st['visits_7d'], users=st['users_7d'], is_new=st['is_new']) if st else None),
        copy=(dict(file=COPIES[slug]['file'], size_bytes=COPIES[slug]['size_bytes']) if slug in COPIES else None),
        data_needs=COPIES.get(slug, {}).get('endpoints', []),
        migration_priority=None,
        notes=[],
    )
    if slug == 'trader-shifts': r['notes'].append('Второй по посещаемости отчёт. Нет ни в навигации, ни в прямых ссылках, копии нет. Данные, судя по Operational, идут через /api/shifts/grid.')
    if slug == 'metrics-legend': r['notes'].append('Канонический глоссарий, 15 метрик. Импортируется в реестр метрик, как отчёт не переносится.')
    if slug == 'threshold-analysis': r['notes'].append('Только для админов. Назначение неизвестно, копии нет. Вероятно связан с Capacity / Threshold.')
    if slug == 'trader-hourly-calculator': r['notes'].append('Копия весит 25 МБ: массив данных вшит в HTML при рендере. Кандидат на починку производительности, но открывался 39 раз в неделю.')
    if slug == 'trader-operational': r['notes'].append('80% всех вызовов API halleg: 26 запросов на загрузку страницы.')
    if slug in ('trader-affiliate-tier', 'trader-merchant-slu', 'traders-active', 'trader-capacity'): r['notes'].append('Ноль посещений за неделю статистики.')
    reports.append(r)

# приоритет переноса: по посещениям, затем всё остальное
ranked = sorted([r for r in reports if r['usage_7d']], key=lambda r: -r['usage_7d']['visits'])
for i, r in enumerate(ranked, 1): r['migration_priority'] = i
n = len(ranked)
for r in reports:
    if r['migration_priority'] is None and r['kind'] == 'report': n += 1; r['migration_priority'] = n

doc = dict(
    _meta=dict(
        generated_by='local/tools/build_reports.py',
        sources=['local/Source/*_files/sidebar.js (META)', 'local/designs/night/index.html (LINKS, REPORTS)', 'local/Source/*.html (saved from url, /api/*)'],
        usage_snapshot='разовая выгрузка за 7 дней, ~31.08-06.09.2026, из макета night; постоянной статистики на halleg нет',
        caveats=['name.uk и name.ru - наш перевод, не из источника', 'department - стартовое предположение, подтверждения нет',
                 'data_needs - что отчёт запрашивает у halleg сейчас; это список потребностей для нашего бэкенда, не контракт'],
    ),
    reports=reports)
json.dump(doc, open(OUT, 'w'), ensure_ascii=False, indent=1)
print(len(reports), 'отчётов')
for r in sorted(reports, key=lambda r: r['migration_priority'] or 99):
    u = r['usage_7d']
    print(f"{r['migration_priority'] or '-':>2} {r['slug']:26} {r['group']:9} nav={int(r['listed_in']['sidebar'])} links={int(r['listed_in']['direct_links'])} stats={int(r['listed_in']['usage_stats'])} copy={int(bool(r['copy']))} eps={len(r['data_needs']):2} {u['visits'] if u else '':>6}")
