"""
imitation.py — модуль «Имитация активности».

Фоновый модуль: выбранные аккаунты в фоне делают человеческие действия
(чтение диалогов, просмотр профилей, скролл) БЕЗ реальных задач рассылки.
Цель — имитировать живых пользователей, чтобы Telegram-алгоритмы видели
естественный паттерн активности и не помечали как ботов.

Использует существующие хелперы из utils/anti_detect.
"""
from __future__ import annotations

import asyncio
import logging
import random
import time
from dataclasses import dataclass, field
from typing import List, Optional

logger = logging.getLogger("telepush.imitation")


@dataclass
class ImitationJob:
    owner_id: int = 0
    phones: List[str] = field(default_factory=list)
    interval_min: int = 15           # мин. интервал активности (мин)
    interval_max: int = 45           # макс. интервал активности (мин)
    actions_per_round: int = 3       # сколько действий за один проход на акк
    do_read: bool = True             # читать сообщения в случайных диалогах
    do_view_profile: bool = True     # смотреть профили из контактов
    do_view_photo: bool = False      # смотреть фото
    status: str = "idle"             # idle / running / stopped
    started_at: float = 0.0
    counts: dict = field(default_factory=lambda: {"reads": 0, "profiles": 0, "photos": 0, "rounds": 0})
    log_lines: List[str] = field(default_factory=list)
    stop_flag: bool = False


async def _do_one_round(acc, job: ImitationJob) -> None:
    """Один проход — выполняет несколько действий на одном аккаунте."""
    if not getattr(acc, 'client', None):
        return

    log = logging.getLogger("telepush.imitation")
    actions = []
    if job.do_read:
        actions.append("read")
    if job.do_view_profile:
        actions.append("profile")
    if job.do_view_photo:
        actions.append("photo")
    if not actions:
        return

    # Берём случайные диалоги
    try:
        dialogs = []
        async for d in acc.client.iter_dialogs(limit=30):
            dialogs.append(d)
    except Exception as e:
        log.debug("[imit] %s — iter_dialogs: %s", acc.phone, e)
        return

    if not dialogs:
        return

    n = min(job.actions_per_round, len(dialogs))
    picks = random.sample(dialogs, n) if len(dialogs) >= n else dialogs

    for d in picks:
        action = random.choice(actions)
        try:
            if action == "read" and d.unread_count and d.unread_count > 0:
                from utils.anti_detect import mark_chat_read
                cnt = await mark_chat_read(acc.client, d.entity, module="imitation")
                if cnt > 0:
                    job.counts["reads"] += cnt
                    job.log_lines.append(f"[{acc.phone}] прочитал {cnt} в «{(d.title or '')[:30]}»")
            elif action == "profile" and d.is_user:
                from utils.anti_detect import view_profile
                ok = await view_profile(acc.client, d.entity, module="imitation",
                                        sample_rate=1.0)  # уже выбран — sample не нужен
                if ok:
                    job.counts["profiles"] += 1
                    job.log_lines.append(f"[{acc.phone}] открыл профиль {(d.title or '')[:30]}")
            elif action == "photo" and d.is_user:
                from utils.anti_detect import view_profile_photo
                ok = await view_profile_photo(acc.client, d.entity, module="imitation",
                                              sample_rate=1.0)
                if ok:
                    job.counts["photos"] += 1
                    job.log_lines.append(f"[{acc.phone}] посмотрел фото {(d.title or '')[:30]}")
        except Exception as e:
            log.debug("[imit] %s — action %s: %s", acc.phone, action, e)
        # Между действиями небольшая пауза — выглядит как живой человек
        await asyncio.sleep(random.uniform(3, 12))

    job.counts["rounds"] += 1
    if len(job.log_lines) > 200:
        job.log_lines = job.log_lines[-150:]


async def run_imitation_job(job: ImitationJob, ctx) -> None:
    """Главный цикл имитации. Запускается в фоне.
    Каждый акк делает actions_per_round действий, потом ждёт случайное время
    между interval_min и interval_max минут до следующего прохода."""
    job.status = "running"
    job.started_at = time.time()
    log = logging.getLogger("telepush.imitation")
    log.info("Имитация запущена для %d акков (интервал %d-%d мин)",
             len(job.phones), job.interval_min, job.interval_max)

    try:
        from core.client_builder import ensure_connected

        while not job.stop_flag:
            for phone in job.phones:
                if job.stop_flag:
                    break
                acc = ctx.accounts.get(phone)
                if not acc or acc.dead or getattr(acc, "frozen", False):
                    continue
                if not await ensure_connected(acc):
                    continue
                try:
                    await _do_one_round(acc, job)
                except Exception as e:
                    log.warning("[imit] %s — раунд упал: %s", phone, e)
                # Пауза между акками — чтобы не лупить все одновременно
                await asyncio.sleep(random.uniform(10, 25))

            if job.stop_flag:
                break

            # Ждём случайное время до следующего прохода
            wait_min = random.uniform(job.interval_min, job.interval_max) * 60
            log.info("Раунд завершён (всего: %d прочитано, %d профилей, %d фото). "
                     "Следующий через ~%.0f мин",
                     job.counts.get("reads", 0), job.counts.get("profiles", 0),
                     job.counts.get("photos", 0), wait_min / 60)
            try:
                await asyncio.sleep(wait_min)
            except asyncio.CancelledError:
                break
    finally:
        job.status = "stopped"
        log.info("Имитация остановлена. Всего: %d прочитано, %d профилей, %d фото",
                 job.counts.get("reads", 0), job.counts.get("profiles", 0),
                 job.counts.get("photos", 0))
