"""
core/job_persistence.py — сохранение и восстановление задач при перезапуске бота.

При остановке бота все running/paused задачи сохраняются в runtime/pending_jobs/.
При старте — загружаются и автоматически возобновляются.
"""
import json
import time
from pathlib import Path
from typing import List, Dict, Optional

from bot_ui.logger_setup import logger
from config import RUNTIME_DIR

PENDING_DIR = RUNTIME_DIR / "pending_jobs"
PENDING_DIR.mkdir(parents=True, exist_ok=True)


def save_pending_jobs():
    """Сохраняет все running/paused задачи всех пользователей в pending_jobs/."""
    import state

    saved = 0
    # Очищаем старые pending
    for f in PENDING_DIR.glob("*.json"):
        try:
            f.unlink()
        except Exception:
            pass

    for uid, ctx in list(state.owners.items()):
        # Join job
        job = getattr(ctx, "job", None)
        if job and job.status in ("running", "paused"):
            remaining = job.queue[job.processed:]
            if remaining:
                snap = {
                    "type": "join",
                    "owner_id": uid,
                    "phone": job.acc.phone,
                    "queue": remaining,
                    "total_original": job.total,
                    "processed": job.processed,
                    "counts": dict(job.counts),
                    "results": dict(job.results),
                    "status": job.status,
                    "started_at": job.started_at,
                    "job_id": job.job_id,
                    "saved_at": time.time(),
                }
                path = PENDING_DIR / f"join_{uid}_{job.acc.phone}.json"
                try:
                    path.write_text(json.dumps(snap, ensure_ascii=False, indent=2), encoding="utf-8")
                    saved += 1
                except Exception as e:
                    logger.warning("[persist] Ошибка сохранения join %s: %s", uid, e)

        # Spam job
        spam_job = getattr(ctx, "spam_job", None)
        if spam_job and spam_job.status in ("running", "paused"):
            remaining_targets = [t for t in spam_job.targets if t not in spam_job.results]
            if remaining_targets:
                snap = {
                    "type": "spam",
                    "owner_id": uid,
                    "phone": spam_job.acc.phone,
                    "text": spam_job.text,
                    "targets": remaining_targets,
                    "titles": {str(k): v for k, v in spam_job.titles.items()},
                    "total_original": spam_job.total,
                    "processed": spam_job.processed,
                    "counts": dict(spam_job.counts),
                    "status": spam_job.status,
                    "started_at": spam_job.started_at,
                    "job_id": spam_job.job_id,
                    "image_path": spam_job.image_path,
                    # 1.5.49 anti-detect и расширенные опции
                    "parse_mode": getattr(spam_job, "parse_mode", None),
                    "forward_from_chat": getattr(spam_job, "forward_from_chat", None),
                    "forward_msg_id": getattr(spam_job, "forward_msg_id", None),
                    "loop_hours": getattr(spam_job, "loop_hours", None),
                    "ad_config": getattr(spam_job, "ad_config", None) or {},
                    "saved_at": time.time(),
                }
                path = PENDING_DIR / f"spam_{uid}_{spam_job.acc.phone}.json"
                try:
                    path.write_text(json.dumps(snap, ensure_ascii=False, indent=2), encoding="utf-8")
                    saved += 1
                except Exception as e:
                    logger.warning("[persist] Ошибка сохранения spam %s: %s", uid, e)

        # DM (user_spam) job
        dm_job = getattr(ctx, "user_spam_job", None)
        if dm_job and dm_job.status in ("running", "paused"):
            remaining_targets = [t for t in dm_job.targets if t not in getattr(dm_job, 'results', set())]
            if remaining_targets:
                snap = {
                    "type": "dm",
                    "owner_id": uid,
                    "phone": dm_job.acc.phone,
                    "text": dm_job.text,
                    "targets": remaining_targets,
                    "total_original": dm_job.total,
                    "processed": dm_job.processed,
                    "counts": dict(dm_job.counts),
                    "status": dm_job.status,
                    "started_at": dm_job.started_at,
                    # 1.5.49 anti-detect
                    "parse_mode": getattr(dm_job, "parse_mode", None),
                    "loop_hours": getattr(dm_job, "loop_hours", None),
                    "ad_config": getattr(dm_job, "ad_config", None) or {},
                    "saved_at": time.time(),
                }
                path = PENDING_DIR / f"dm_{uid}_{dm_job.acc.phone}.json"
                try:
                    path.write_text(json.dumps(snap, ensure_ascii=False, indent=2), encoding="utf-8")
                    saved += 1
                except Exception as e:
                    logger.warning("[persist] Ошибка сохранения dm %s: %s", uid, e)

        # Multi jobs
        multi_jobs = ctx.temp.get("multi_jobs") or []
        for mj in multi_jobs:
            if not hasattr(mj, "status") or mj.status not in ("running", "paused"):
                continue
            phone = getattr(mj, "acc", None)
            if not phone:
                continue
            phone = mj.acc.phone
            if hasattr(mj, "queue"):
                # JoinJob
                remaining = mj.queue[mj.processed:]
                if remaining:
                    snap = {
                        "type": "join",
                        "owner_id": uid,
                        "phone": phone,
                        "queue": remaining,
                        "total_original": mj.total,
                        "processed": mj.processed,
                        "counts": dict(mj.counts),
                        "results": dict(mj.results),
                        "status": mj.status,
                        "started_at": mj.started_at,
                        "job_id": getattr(mj, "job_id", None),
                        "saved_at": time.time(),
                    }
                    path = PENDING_DIR / f"join_{uid}_{phone}.json"
                    try:
                        path.write_text(json.dumps(snap, ensure_ascii=False, indent=2), encoding="utf-8")
                        saved += 1
                    except Exception as e:
                        logger.warning("[persist] Ошибка сохранения multi-join: %s", e)

    if saved:
        logger.info("[persist] 💾 Сохранено %d незавершённых задач", saved)
    return saved


MAX_PENDING_AGE_SEC = 12 * 3600  # 12 hours max

def load_pending_jobs() -> List[Dict]:
    """Загружает все pending задачи из runtime/pending_jobs/. Удаляет старше 12ч."""
    jobs = []
    now = time.time()
    for f in PENDING_DIR.glob("*.json"):
        try:
            data = json.loads(f.read_text(encoding="utf-8"))
            # Validate required fields
            if not isinstance(data, dict) or "type" not in data or "owner_id" not in data:
                logger.warning("[persist] Некорректный job файл %s: отсутствуют обязательные поля", f.name)
                f.unlink(missing_ok=True)
                continue
            saved_at = data.get("saved_at", 0)
            if saved_at and (now - saved_at) > MAX_PENDING_AGE_SEC:
                logger.info("[persist] Удаляю устаревшую задачу %s (%.1f ч назад)", f.name, (now - saved_at) / 3600)
                f.unlink(missing_ok=True)
                continue
            jobs.append(data)
        except Exception as e:
            logger.warning("[persist] Ошибка загрузки %s: %s", f.name, e)
            try:
                f.unlink(missing_ok=True)
            except Exception:
                pass
    return jobs


def clear_pending_jobs():
    """Очищает pending задачи после успешного восстановления."""
    for f in PENDING_DIR.glob("*.json"):
        try:
            f.unlink()
        except Exception:
            pass


async def restore_jobs_for_user(uid: int):
    """Очищает сохранённые задачи при старте (не восстанавливает автоматически).

    Аккаунты не будятся сами — пользователь сам запускает задачи через UI.
    """
    pending = load_pending_jobs()
    user_jobs = [j for j in pending if j.get("owner_id") == uid]

    if not user_jobs:
        return 0

    # Очищаем файлы — задачи НЕ восстанавливаем, чтобы не будить аккаунты
    cleared = 0
    for f in PENDING_DIR.glob(f"*_{uid}_*.json"):
        try:
            f.unlink(missing_ok=True)
            cleared += 1
        except Exception:
            pass

    for snap in user_jobs:
        phone = snap.get("phone")
        job_type = snap.get("type", "join")
        if job_type == "join":
            queue_len = len(snap.get("queue", []))
            logger.info("[restore] Очищена незавершённая подписка %s: %d ссылок (не восстановлена)", phone, queue_len)
        elif job_type == "spam":
            targets_len = len(snap.get("targets", []))
            logger.info("[restore] Очищена незавершённая рассылка %s: %d чатов (не восстановлена)", phone, targets_len)

    return 0
