#!/usr/bin/env python3
from __future__ import annotations

from pathlib import Path
from datetime import datetime, timezone
import zipfile
import os

ROOT = Path('/opt/data/vercel-project')
ARCH = ROOT / 'assets' / 'archives'
ARCH.mkdir(parents=True, exist_ok=True)

INITIAL = ARCH / 'schedule-site_initial.zip'
WEEKLY = ARCH / 'schedule-site_weekly.zip'
DAILY = ARCH / 'schedule-site_daily.zip'
MANIFEST = ARCH / 'archive-manifest.txt'

EXCLUDE_PREFIXES = (
    '.git/',
    '.vercel/',
    'assets/archives/',
)


def build_zip(target: Path) -> int:
    tmp = target.with_suffix('.tmp')
    if tmp.exists():
        tmp.unlink()
    with zipfile.ZipFile(tmp, 'w', zipfile.ZIP_DEFLATED) as z:
        for p in ROOT.rglob('*'):
            if not p.is_file():
                continue
            rel = p.relative_to(ROOT).as_posix()
            if any(rel.startswith(prefix) for prefix in EXCLUDE_PREFIXES):
                continue
            z.write(p, rel)
    tmp.replace(target)
    return target.stat().st_size


def is_older_than_days(path: Path, days: int) -> bool:
    if not path.exists():
        return True
    age = datetime.now(timezone.utc).timestamp() - path.stat().st_mtime
    return age >= days * 24 * 60 * 60


def prune_old_archives() -> list[str]:
    keep = {INITIAL.name, WEEKLY.name, DAILY.name, MANIFEST.name}
    removed = []
    for p in ARCH.glob('*'):
        if p.is_file() and p.name not in keep:
            p.unlink()
            removed.append(p.name)
    return removed


def fmt(path: Path) -> str:
    if not path.exists():
        return 'немає'
    dt = datetime.fromtimestamp(path.stat().st_mtime).strftime('%d.%m.%Y %H:%M')
    size_mb = path.stat().st_size / 1024 / 1024
    return f'{dt}, {size_mb:.1f} МБ'


def write_archive_page():
    css = '''.nav{display:flex;gap:8px;justify-content:center;flex-wrap:wrap;margin-bottom:14px;position:sticky;top:0;z-index:999;background:rgba(255,255,255,.97);padding:8px;border-bottom:1px solid #e8e8e8}.nav a{background:#4472C4;color:white;text-decoration:none;padding:9px 12px;border-radius:8px;font-size:13px;font-weight:800;box-shadow:0 2px 6px rgba(0,0,0,.12)}.nav a.active{background:#1f4e9e;box-shadow:inset 0 0 0 2px rgba(255,255,255,.55),0 2px 6px rgba(0,0,0,.12)}.nav a:hover{background:#2f62b3}.site-logos{display:grid;grid-template-columns:minmax(120px,1fr) auto minmax(120px,1fr);align-items:center;gap:14px;margin:0 0 8px 0;padding:2px 4px;min-height:50px;pointer-events:none}.site-logos img{display:block;object-fit:contain;opacity:.94;filter:grayscale(100%);width:auto}.site-logos .logo-left{justify-self:start;max-width:300px;max-height:44px}.site-logos .logo-center{justify-self:center;max-width:150px;max-height:62px}.site-logos .logo-right{justify-self:end;max-width:340px;max-height:44px}*{box-sizing:border-box;margin:0;padding:0}body{font-family:Arial,sans-serif;background:#f0f2f5;padding:10px;color:#1f2937}.container{max-width:1050px;margin:0 auto;background:#fff;border-radius:10px;box-shadow:0 2px 12px rgba(0,0,0,.1);padding:15px;min-height:90vh}h1{text-align:center;margin:10px 0 8px;font-size:24px}.subtitle{text-align:center;color:#667085;margin-bottom:16px}.panel{border:1px solid #d8dee9;border-radius:12px;background:#fff;padding:16px;margin:12px auto;max-width:860px;box-shadow:0 2px 10px rgba(0,0,0,.04)}.archive-card{border:1px solid #d8dee9;border-radius:12px;padding:14px;margin:10px 0;background:#fafafa}.archive-card strong{display:block;font-size:18px;margin-bottom:5px}.archive-card p{color:#667085;line-height:1.45;margin:5px 0}.btn{display:inline-block;background:#1f4e9e;color:#fff;text-decoration:none;border:0;border-radius:9px;padding:10px 14px;font-weight:900;margin-top:8px}.btn.red{background:#d92d20}.btn.gray{background:#64748b}.notice{background:#fff7ed;border:1px solid #fdba74;color:#7c2d12;border-radius:10px;padding:12px;margin:10px 0;line-height:1.45}.footer{text-align:center;color:#999;font-size:11px;margin-top:18px}@media(max-width:700px){.nav{gap:5px}.nav a{font-size:11px;padding:7px 8px}.container{padding:10px}.panel{padding:12px}.site-logos{grid-template-columns:1fr auto 1fr;min-height:36px;margin-bottom:6px;gap:6px}.site-logos .logo-left{max-width:115px;max-height:30px}.site-logos .logo-center{max-width:82px;max-height:44px}.site-logos .logo-right{max-width:125px;max-height:30px}}'''
    nav = '<div class="nav"><a href="/">Графік на місяць</a><a href="/daily.html">Щоденний графік</a><a href="/timesheet.html">Квиток контролера</a><a href="/stt.html">Звіт СТТ</a><a href="/vacations.html">Відпустки</a><a href="/archive.html" class="active">Архів</a><a href="/voice.html">Голосові команди</a><a href="/editor.html">Редактор</a></div>'
    logos = '<div class="site-logos"><img class="logo-left" src="/assets/header-left.jpg" alt="КП ЧЕТС"><img class="logo-center" src="/assets/trolleybus-center.jpg" alt="Тролейбус"><img class="logo-right" src="/assets/header-right.jpg" alt="Черкаси — найкраще місто"></div>'
    html = f'''<!DOCTYPE html><html lang="uk"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Архів сайту</title><style>{css}</style></head><body><div class="container">{nav}{logos}<h1>📦 Архів сайту</h1><p class="subtitle">Тут зберігаються тільки три резервні копії сайту.</p><div class="panel" id="rescue-panel"><h2>Резервне дзеркало і аварійний доступ</h2><p><a class="btn gray" href="/voice.html">Голосові команди для правок</a></p><p>Якщо основний Vercel-сайт тимчасово не відкривається, використовуйте резервну статичну копію на GitHub Pages.</p><p><a class="btn" href="https://chets-schedule-site.pages.dev">Відкрити резервне дзеркало</a> <a class="btn gray" href="/emergency.html">Аварійна інструкція</a></p><p style="color:#667085;line-height:1.45;margin-top:8px">Основне резервне дзеркало Cloudflare Pages уже працює для перегляду графіків. GitHub Pages-дубль також підготовлений у гілці gh-pages, але може потребувати одноразового ввімкнення в Settings → Pages. Публікація змін через редактор працює на основному Vercel-сайті; офлайн-зміни можна зберегти пакетом JSON.</p></div><div class="panel"><div class="notice"><b>Правило архівів:</b><br>1 головний відновлювальний архів з поточним стабільним станом; 1 тижневий архів оновлюється приблизно раз на 7 днів; 1 щоденний архів перезаписується щодня. Старі датовані архіви автоматично прибираються.</div><div class="archive-card"><strong>1. Головний відновлювальний архів</strong><p>Основна резервна копія поточного стабільного сайту для швидкого відновлення.</p><p><b>Стан:</b> {fmt(INITIAL)}</p><a class="btn gray" href="/assets/archives/{INITIAL.name}" download>⬇️ Скачати головний архів</a></div><div class="archive-card"><strong>2. Тижневий архів</strong><p>Оновлюється не частіше ніж раз на тиждень.</p><p><b>Стан:</b> {fmt(WEEKLY)}</p><a class="btn" href="/assets/archives/{WEEKLY.name}" download>⬇️ Скачати тижневий архів</a></div><div class="archive-card"><strong>3. Щоденний архів</strong><p>Перезаписується щодня автоматично.</p><p><b>Стан:</b> {fmt(DAILY)}</p><a class="btn red" href="/assets/archives/{DAILY.name}" download>⬇️ Скачати щоденний архів</a></div></div><p class="footer">КП ЧЕТС | Архів сайту</p></div></body></html>'''
    (ROOT / 'archive.html').write_text(html, encoding='utf-8')


def main():
    now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    removed = prune_old_archives()
    created = []
    if os.environ.get('FORCE_MAIN_RESTORE') == '1':
        created.append(f'main_restore={build_zip(INITIAL)}')
    elif not INITIAL.exists():
        created.append(f'initial={build_zip(INITIAL)}')
    if os.environ.get('FORCE_WEEKLY') == '1' or is_older_than_days(WEEKLY, 7):
        created.append(f'weekly={build_zip(WEEKLY)}')
    created.append(f'daily={build_zip(DAILY)}')
    write_archive_page()
    MANIFEST.write_text(
        f'updated={now}\n' +
        f'initial={fmt(INITIAL)}\nweekly={fmt(WEEKLY)}\ndaily={fmt(DAILY)}\n' +
        'kept=schedule-site_initial.zip,schedule-site_weekly.zip,schedule-site_daily.zip\n' +
        ('removed=' + ','.join(removed) + '\n' if removed else ''),
        encoding='utf-8'
    )
    if os.environ.get('ROLLING_ARCHIVE_VERBOSE'):
        print('rolling archive ok', '; '.join(created))


if __name__ == '__main__':
    main()
