#=============================================================
###  CREATED AT     : 24 FEBRUARI 2026                         
###  UPDATED AT     : 24 FEBRUARI 2026                          
###  COPYRIGHT      : BRIBRAIN DATA ENGINEER TEAM           
###  DESCRIPTION    : Module untuk kumpulan metadata        
#=============================================================

import os
import io
import mimetypes
from typing import Optional, Union, Dict, Any

import requests


TELEGRAM_API = "https://api.telegram.org/bot{token}/{method}"

## ===================================================================================================

def _post_telegram(
    token: str,
    method: str,
    data: Optional[Dict[str, Any]] = None,
    files: Optional[Dict[str, Any]] = None,
    proxies: Optional[Dict[str, str]] = None,
    timeout: int = 20,
) -> Dict[str, Any]:
    """
    Mengirim HTTP POST request ke Telegram Bot API.

    Fungsi ini merupakan helper internal yang digunakan oleh
    fungsi pengiriman Telegram lainnya. Fungsi akan melakukan
    validasi response HTTP dan memastikan Telegram mengembalikan
    status sukses.

    Args:
        token (str):
            Token bot Telegram yang diperoleh dari BotFather.

        method (str):
            Nama method Telegram API.
            Contoh: "sendMessage", "sendPhoto".

        data (dict, optional):
            Payload form data yang dikirim ke Telegram.

        files (dict, optional):
            File yang dikirim menggunakan multipart/form-data.

        proxies (dict, optional):
            Konfigurasi proxy untuk request.

        timeout (int, default=20):
            Batas waktu request dalam detik.

    Returns:
        dict:
            Response JSON dari Telegram yang sudah diparsing.

    Raises:
        requests.HTTPError:
            Jika Telegram mengembalikan status HTTP error.

        RuntimeError:
            Jika Telegram merespon dengan ok=False.
    """

    url = TELEGRAM_API.format(token=token, method=method)

    response = requests.post(
        url,
        data=data or {},
        files=files,
        proxies=proxies,
        timeout=timeout,
    )

    response.raise_for_status()

    payload = response.json()

    if not payload.get("ok"):
        raise RuntimeError(f"Telegram API error: {payload}")

    return payload

## ===================================================================================================

def send_message_telegram(
    message: str,
    bot_token: str,
    chat_id: Union[str, int],
    parse_mode: str = "HTML",
    disable_web_page_preview: bool = True,
    proxies: Optional[Dict[str, str]] = None,
    timeout: int = 20,
    max_len: int = 4096,
) -> bool:
    """
    Mengirim pesan teks ke Telegram.

    Fungsi ini otomatis membagi pesan panjang menjadi beberapa
    bagian jika melebihi batas maksimum Telegram (4096 karakter).

    Args:
        message (str):
            Teks pesan yang ingin dikirim.

        bot_token (str):
            Token bot Telegram.

        chat_id (str | int):
            ID chat tujuan, group, atau channel.

        parse_mode (str, default="HTML"):
            Mode formatting pesan.

            Pilihan:
            - "HTML"
            - "Markdown"
            - "MarkdownV2"

        disable_web_page_preview (bool, default=True):
            Menonaktifkan preview link.

        proxies (dict, optional):
            Konfigurasi proxy.

        timeout (int, default=20):
            Timeout request dalam detik.

        max_len (int, default=4096):
            Panjang maksimum tiap pesan.

    Returns:
        bool:
            True jika seluruh pesan berhasil dikirim.

    Example:
        Kirim pesan sederhana:

        >>> send_message_telegram(
        >>>     message="<b>ETL Success</b>",
        >>>     bot_token=TOKEN,
        >>>     chat_id=CHAT_ID
        >>> )

        Kirim alert error:

        >>> send_message_telegram(
        >>>     message="🔥 <b>Pipeline Failed</b>",
        >>>     bot_token=TOKEN,
        >>>     chat_id=CHAT_ID
        >>> )
    """
    if message is None:
        message = ""

    chunks = [message[i:i + max_len] for i in range(0, len(message), max_len)] or [""]

    for chunk in chunks:
        _post_telegram(
            token=bot_token,
            method="sendMessage",
            data={
                "chat_id": str(chat_id),
                "text": chunk,
                "parse_mode": parse_mode,
                "disable_web_page_preview": disable_web_page_preview,
            },
            proxies=proxies,
            timeout=timeout,
        )

    return True

## ===================================================================================================

def send_image_telegram(
    bot_token: str,
    chat_id: Union[str, int],
    image: Union[str, bytes, io.BytesIO],
    caption: Optional[str] = None,
    parse_mode: str = "HTML",
    filename: str = "image.png",
    proxies: Optional[Dict[str, str]] = None,
    timeout: int = 60,
) -> bool:
    """
    Mengirim gambar ke Telegram.

    Mendukung pengiriman dari file lokal maupun gambar
    hasil generate di memory (bytes / BytesIO).

    Args:
        bot_token (str):
            Token bot Telegram.

        chat_id (str | int):
            Chat tujuan.

        image (str | bytes | BytesIO):
            Sumber gambar.

            Bisa berupa:
            - Path file lokal
            - Raw bytes
            - Object BytesIO

        caption (str, optional):
            Teks caption di bawah gambar.

        parse_mode (str, default="HTML"):
            Mode formatting caption.

        filename (str, default="image.png"):
            Nama file saat upload dari memory.

        proxies (dict, optional):
            Konfigurasi proxy.

        timeout (int, default=60):
            Timeout upload.

    Returns:
        bool:
            True jika gambar berhasil dikirim.

    Example:
        Kirim gambar dari file lokal:

        >>> send_image_telegram(
        >>>     bot_token=TOKEN,
        >>>     chat_id=CHAT_ID,
        >>>     image="report.png",
        >>>     caption="📊 Daily Report"
        >>> )

        Kirim gambar hasil generate matplotlib:

        >>> import io
        >>> buf = io.BytesIO()
        >>> plt.savefig(buf, format="png")
        >>> buf.seek(0)

        >>> send_image_telegram(
        >>>     bot_token=TOKEN,
        >>>     chat_id=CHAT_ID,
        >>>     image=buf
        >>> )
    """
    if isinstance(image, str):
        file_handle = open(image, "rb")
        close_after = True
        name = os.path.basename(image)
    else:
        close_after = False
        file_handle = io.BytesIO(image) if isinstance(image, bytes) else image
        file_handle.seek(0)
        name = filename

    mime = mimetypes.guess_type(name)[0] or "application/octet-stream"

    try:
        data = {"chat_id": str(chat_id)}

        if caption:
            data.update({
                "caption": caption,
                "parse_mode": parse_mode
            })

        _post_telegram(
            token=bot_token,
            method="sendPhoto",
            data=data,
            files={"photo": (name, file_handle, mime)},
            proxies=proxies,
            timeout=timeout,
        )

        return True

    finally:
        if close_after:
            file_handle.close()

## ===================================================================================================

def send_file_telegram(
    bot_token: str,
    chat_id: Union[str, int],
    file_obj: Union[str, bytes, io.BytesIO],
    filename: str,
    caption: Optional[str] = None,
    parse_mode: str = "HTML",
    proxies: Optional[Dict[str, str]] = None,
    timeout: int = 120,
) -> bool:
    """
    Mengirim file atau dokumen ke Telegram.

    Cocok untuk mengirim report, log, CSV, Excel,
    atau file hasil generate lainnya.

    Args:
        bot_token (str):
            Token bot Telegram.

        chat_id (str | int):
            Chat tujuan.

        file_obj (str | bytes | BytesIO):
            Sumber file.

            Bisa berupa:
            - Path file lokal
            - Raw bytes
            - Object BytesIO

        filename (str):
            Nama file yang akan tampil di Telegram.

        caption (str, optional):
            Caption file.

        parse_mode (str, default="HTML"):
            Mode formatting caption.

        proxies (dict, optional):
            Konfigurasi proxy.

        timeout (int, default=120):
            Timeout upload.

    Returns:
        bool:
            True jika file berhasil dikirim.

    Example:
        Kirim CSV:

        >>> send_file_telegram(
        >>>     bot_token=TOKEN,
        >>>     chat_id=CHAT_ID,
        >>>     file_obj="report.csv",
        >>>     filename="report.csv"
        >>> )

        Kirim file dari pandas:

        >>> csv_bytes = df.to_csv(index=False).encode("utf-8")

        >>> send_file_telegram(
        >>>     bot_token=TOKEN,
        >>>     chat_id=CHAT_ID,
        >>>     file_obj=csv_bytes,
        >>>     filename="report.csv"
        >>> )
    """
    if isinstance(file_obj, str):
        file_handle = open(file_obj, "rb")
        close_after = True
        name = os.path.basename(file_obj)
    else:
        close_after = False
        file_handle = io.BytesIO(file_obj) if isinstance(file_obj, bytes) else file_obj
        file_handle.seek(0)
        name = filename

    mime = mimetypes.guess_type(name)[0] or "application/octet-stream"

    try:
        data = {"chat_id": str(chat_id)}

        if caption:
            data.update({
                "caption": caption,
                "parse_mode": parse_mode
            })

        _post_telegram(
            token=bot_token,
            method="sendDocument",
            data=data,
            files={"document": (name, file_handle, mime)},
            proxies=proxies,
            timeout=timeout,
        )

        return True

    finally:
        if close_after:
            file_handle.close()

## ===================================================================================================
