"""
profile_bulk.py — массовое заполнение профилей из папки с материалами.

Структура папки:
  Заполнение профилей/
  ├── Женские/   (опционально)
  │   ├── Имена.txt
  │   ├── Фамилии.txt
  │   ├── Био.txt
  │   ├── Аватарки/   (jpg/png)
  │   └── Сторис/     (опционально)
  └── Мужские/   (аналогично)

Если подпапка одного пола пустая — берёт из противоположного. Если совсем
ничего не нашлось — пропускает обновление этого поля.
"""
from __future__ import annotations

import logging
import random
import shutil
from dataclasses import dataclass, field
from pathlib import Path
from typing import Dict, List, Optional, Tuple

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


@dataclass
class MaterialPool:
    """Пул материалов одного пола (или общий)."""
    names: List[str] = field(default_factory=list)
    surnames: List[str] = field(default_factory=list)
    bios: List[str] = field(default_factory=list)
    avatars: List[Path] = field(default_factory=list)
    stories: List[Path] = field(default_factory=list)


def _read_lines(p: Path) -> List[str]:
    if not p.exists() or not p.is_file():
        return []
    # Попытка определить кодировку — utf-8 / utf-8-sig / cp1251 (Windows)
    for enc in ("utf-8-sig", "utf-8", "cp1251"):
        try:
            text = p.read_text(encoding=enc)
            return [s.strip() for s in text.splitlines() if s.strip()]
        except UnicodeDecodeError:
            continue
        except Exception as e:
            logger.debug("[bulk] read %s: %s", p, e)
            return []
    return []


def _list_images(folder: Path) -> List[Path]:
    if not folder.exists() or not folder.is_dir():
        return []
    exts = {".jpg", ".jpeg", ".png", ".webp"}
    return sorted([f for f in folder.iterdir() if f.is_file() and f.suffix.lower() in exts])


def _list_media(folder: Path) -> List[Path]:
    """Включая видео для сторис."""
    if not folder.exists() or not folder.is_dir():
        return []
    exts = {".jpg", ".jpeg", ".png", ".webp", ".mp4", ".mov"}
    return sorted([f for f in folder.iterdir() if f.is_file() and f.suffix.lower() in exts])


def load_pool(folder: Path) -> MaterialPool:
    """Загружает один пул из подпапки."""
    pool = MaterialPool()
    if not folder.exists() or not folder.is_dir():
        return pool
    pool.names = _read_lines(folder / "Имена.txt")
    pool.surnames = _read_lines(folder / "Фамилии.txt")
    pool.bios = _read_lines(folder / "Био.txt")
    pool.avatars = _list_images(folder / "Аватарки")
    pool.stories = _list_media(folder / "Сторис")
    return pool


def scan_materials(root: Path) -> Dict:
    """Сканирует папку с материалами, возвращает что нашлось.

    Структура: { "female": {names, surnames, ...counts}, "male": {...}, "general": {...} }
    """
    if not root.exists():
        return {"error": f"Папка не существует: {root}"}

    result = {}
    for label, sub in [("female", "Женские"), ("male", "Мужские"), ("general", "")]:
        sub_path = root / sub if sub else root
        if not sub_path.exists():
            continue
        pool = load_pool(sub_path)
        result[label] = {
            "path": str(sub_path),
            "names": len(pool.names),
            "surnames": len(pool.surnames),
            "bios": len(pool.bios),
            "avatars": len(pool.avatars),
            "stories": len(pool.stories),
        }
    return result


def pick_materials(pool: Optional[MaterialPool], fallback: Optional[MaterialPool] = None) -> Dict:
    """Берёт случайный комплект из пула (с фолбеком в общий)."""
    out = {"first_name": None, "last_name": None, "bio": None, "avatar": None, "stories": []}
    sources = [p for p in (pool, fallback) if p is not None]

    for s in sources:
        if out["first_name"] is None and s.names:
            out["first_name"] = random.choice(s.names)
        if out["last_name"] is None and s.surnames:
            out["last_name"] = random.choice(s.surnames)
        if out["bio"] is None and s.bios:
            out["bio"] = random.choice(s.bios)[:70]
        if out["avatar"] is None and s.avatars:
            out["avatar"] = random.choice(s.avatars)
        if not out["stories"] and s.stories:
            n = min(len(s.stories), random.randint(1, 3))
            out["stories"] = random.sample(s.stories, n)
    return out


def consume_avatar(pool: MaterialPool, avatar: Path, used_dir: Path):
    """Перемещает использованную аватарку в подпапку used/."""
    if not avatar.exists():
        return
    used_dir.mkdir(parents=True, exist_ok=True)
    dst = used_dir / avatar.name
    try:
        shutil.move(str(avatar), str(dst))
        # Удаляем из пула чтобы не выбирать снова
        if avatar in pool.avatars:
            pool.avatars.remove(avatar)
    except Exception as e:
        logger.debug("[bulk] consume %s: %s", avatar, e)


async def bulk_fill_account(client, materials: Dict, options: Dict) -> Dict:
    """Применяет комплект материалов к одному аккаунту.

    options: { do_name, do_bio, do_avatar, do_stories, delete_old_avatars }
    Возвращает {"ok": bool, "actions": [str], "error": str}
    """
    from telethon.tl.functions.account import UpdateProfileRequest
    from telethon.tl.functions.photos import UploadProfilePhotoRequest, GetUserPhotosRequest, DeletePhotosRequest
    from telethon.tl.types import InputPhoto

    actions = []
    try:
        # 1. Имя/фамилия
        if options.get("do_name") and (materials.get("first_name") or materials.get("last_name")):
            kw = {}
            if materials.get("first_name"):
                kw["first_name"] = materials["first_name"]
            if materials.get("last_name"):
                kw["last_name"] = materials["last_name"]
            await client(UpdateProfileRequest(**kw))
            actions.append(f"имя={materials.get('first_name','-')} {materials.get('last_name','-')}")

        # 2. Био
        if options.get("do_bio") and materials.get("bio"):
            await client(UpdateProfileRequest(about=materials["bio"]))
            actions.append(f"био='{materials['bio'][:30]}'")

        # 3. Удалить старые аватарки
        if options.get("delete_old_avatars"):
            try:
                me = await client.get_me()
                photos = await client(GetUserPhotosRequest(user_id=me, offset=0, max_id=0, limit=100))
                inputs = [InputPhoto(id=ph.id, access_hash=ph.access_hash, file_reference=ph.file_reference)
                          for ph in (photos.photos or [])]
                if inputs:
                    await client(DeletePhotosRequest(id=inputs))
                    actions.append(f"удалил {len(inputs)} старых фото")
            except Exception as e:
                logger.debug("[bulk] delete photos: %s", e)

        # 4. Новая аватарка
        if options.get("do_avatar") and materials.get("avatar"):
            avatar_path = Path(materials["avatar"])
            if avatar_path.exists():
                up = await client.upload_file(str(avatar_path))
                await client(UploadProfilePhotoRequest(file=up))
                actions.append(f"аватарка={avatar_path.name}")

        # 5. Сторис
        if options.get("do_stories") and materials.get("stories"):
            try:
                from telethon.tl.functions.stories import SendStoryRequest
                from telethon.tl.types import InputPrivacyValueAllowAll, InputMediaUploadedPhoto
                privacy = [InputPrivacyValueAllowAll()]
                for media_path in materials["stories"]:
                    p = Path(media_path)
                    if not p.exists():
                        continue
                    up = await client.upload_file(str(p))
                    is_video = p.suffix.lower() in {".mp4", ".mov"}
                    if is_video:
                        from telethon.tl.types import InputMediaUploadedDocument, DocumentAttributeVideo
                        media = InputMediaUploadedDocument(
                            file=up, mime_type="video/mp4",
                            attributes=[DocumentAttributeVideo(duration=0, w=720, h=1280, supports_streaming=True)],
                        )
                    else:
                        media = InputMediaUploadedPhoto(file=up)
                    try:
                        await client(SendStoryRequest(peer="me", media=media, privacy_rules=privacy))
                        actions.append(f"сторис={p.name}")
                    except Exception as se:
                        logger.debug("[bulk] story upload %s: %s", p.name, se)
            except ImportError:
                logger.debug("[bulk] SendStoryRequest не доступен в этой версии Telethon")

        return {"ok": True, "actions": actions, "error": ""}
    except Exception as e:
        return {"ok": False, "actions": actions, "error": str(e)[:120]}
