from __future__ import annotations

import html
import io
import logging
import os
import secrets
from string import Formatter

from src.config import settings
from src.repositories import repo
from src.services import log_service

log = logging.getLogger(__name__)
_ALLOWED_FIELDS = {"link", "title", "category"}


def make_code() -> str:
    return secrets.token_hex(16)


def deep_link(bot, code: str) -> str:
    return f"https://t.me/{bot.get_me().username}?start={code}"


def validate_channel_template(template: str) -> None:
    fields = {name for _, name, _, _ in Formatter().parse(template) if name}
    unknown = fields - _ALLOWED_FIELDS
    if unknown:
        raise ValueError("Placeholder tidak dikenal: " + ", ".join(sorted(unknown)))
    if "link" not in fields:
        raise ValueError("Caption channel wajib memiliki placeholder {link}")


def channel_caption(bot, media) -> str:
    template = repo.setting("channel_caption_template")
    validate_channel_template(template)
    return template.format(
        link=deep_link(bot, media["code"]),
        title=html.escape(media["title"]),
        category=html.escape(media["category"]),
    )



def replace_file_name(bot, mid: int, new_filename: str, staging_chat_id: int) -> str:
    """Unggah ulang file sebagai dokumen agar nama file Telegram benar-benar berubah."""
    media = repo.media_by_id(mid)
    if not media:
        raise ValueError("Media tidak ditemukan")

    safe_name = os.path.basename((new_filename or "").strip())
    if not safe_name or safe_name in {".", ".."}:
        raise ValueError("Nama file tidak valid")
    if len(safe_name) > 255:
        raise ValueError("Nama file maksimal 255 karakter")

    staged_message = None
    try:
        file_info = bot.get_file(media["telegram_file_id"])
        file_bytes = bot.download_file(file_info.file_path)
        stream = io.BytesIO(file_bytes)
        stream.name = safe_name
        staged_message = bot.send_document(
            staging_chat_id,
            stream,
            visible_file_name=safe_name,
            caption="⏳ Memproses perubahan nama file...",
        )
        if not staged_message.document:
            raise RuntimeError("Telegram tidak mengembalikan file dokumen baru")

        repo.update_media(mid, "telegram_file_id", staged_message.document.file_id)
        repo.update_media(mid, "filename", safe_name)
        return staged_message.document.file_id
    except Exception as exc:
        raise RuntimeError(
            "Nama file gagal diterapkan. Telegram harus mengunduh dan mengunggah ulang "
            "file; pastikan ukuran file masih dapat diunduh oleh Bot API."
        ) from exc
    finally:
        if staged_message is not None:
            try:
                bot.delete_message(staging_chat_id, staged_message.message_id)
            except Exception:
                log.warning("Pesan sementara rename file tidak dapat dihapus", exc_info=True)

def publish(bot, mid: int, replace_old: bool = False):
    media = repo.media_by_id(mid)
    if not media:
        raise ValueError("Media tidak ditemukan")
    if not settings.channel_post_id:
        raise RuntimeError("CHANNEL_POST_ID belum dikonfigurasi")

    old_chat_id = media["channel_chat_id"] or settings.channel_post_id
    old_message_id = media["channel_message_id"]
    caption = channel_caption(bot, media)

    try:
        if media["thumbnail_file_id"]:
            new_message = bot.send_photo(
                settings.channel_post_id,
                media["thumbnail_file_id"],
                caption=caption,
                parse_mode="HTML",
            )
        else:
            new_message = bot.send_message(
                settings.channel_post_id,
                caption,
                parse_mode="HTML",
                disable_web_page_preview=True,
            )
    except Exception:
        log.exception("Gagal posting media %s", mid)
        log_service.send(bot, "❌ Posting media gagal", f"Media ID: {mid}")
        raise

    repo.set_channel_post(mid, settings.channel_post_id, new_message.message_id)

    # Hapus posting lama hanya setelah posting baru benar-benar berhasil.
    if replace_old and old_message_id and old_message_id != new_message.message_id:
        try:
            bot.delete_message(old_chat_id, old_message_id)
        except Exception:
            log.warning("Posting lama media %s tidak dapat dihapus", mid, exc_info=True)

    log_service.send(bot, "✅ Media diposting", f"Media ID: {mid}\nJudul: {media['title']}")
    return new_message


def remove(bot, mid: int) -> None:
    media = repo.media_by_id(mid)
    if not media:
        raise ValueError("Media tidak ditemukan")
    if media["channel_message_id"]:
        try:
            bot.delete_message(
                media["channel_chat_id"] or settings.channel_post_id,
                media["channel_message_id"],
            )
        except Exception:
            log.warning("Posting channel tidak dapat dihapus", exc_info=True)
    repo.delete_media(mid)
    log_service.send(bot, "🗑 Media dihapus", f"Media ID: {mid}\nJudul: {media['title']}")
