import asyncio
import pymysql

from telethon import TelegramClient, events, functions
from telethon.errors import FloodWaitError, RPCError

CHANNEL = "doostanone"
API_ID = 12228029
API_HASH = "9bf063457a4fa9e962a8bf5e45dbc487"
PHONE = "+989156247445"

TG_PASSWORD = "Hi110100159&"
DB_CONFIG = {
    "host": "localhost",
    "user": "noskheha_ktbm",
    "password": "4VCt;Bq_nL={.+UX",
    "database": "noskheha_ktbm",
    "charset": "utf8mb4",
    "cursorclass": pymysql.cursors.Cursor,
    "autocommit": True
}

# -*- coding: utf-8 -*-
"""
Telegram Channel Sync Bot
- Sync all messages from a public channel into MySQL
- Resume automatically from last saved message_id
- Handle new messages, edits, and deletions
- Designed for shared hosting (cPanel / CloudLinux)
- Uses Telethon with a single event loop to avoid:
  RuntimeError: Future attached to a different loop
"""

SESSION = "enterprise_session"

BATCH_SIZE = 10
SLEEP_BETWEEN_BATCH = 2

# =========================================================
# TELETHON CLIENT
# =========================================================
client = TelegramClient(
    SESSION,
    API_ID,
    API_HASH,
    auto_reconnect=True,
    connection_retries=None,   # infinite retries
    retry_delay=5
)

# =========================================================
# DATABASE
# =========================================================
def get_connection():
    return pymysql.connect(**DB_CONFIG)


def get_last_message_id():
    conn = get_connection()
    try:
        with conn.cursor() as cur:
            cur.execute("SELECT COALESCE(MAX(message_id), 0) FROM messages")
            row = cur.fetchone()
            return int(row[0] or 0)
    finally:
        conn.close()


def save_message(msg, channel_id):
    conn = get_connection()
    try:
        with conn.cursor() as cur:
            text = msg.message or ""

            # message type
            if msg.photo:
                msg_type = "photo"
            elif msg.audio:
                msg_type = "audio"
            elif msg.video:
                msg_type = "video"
            elif msg.document:
                msg_type = "document"
            else:
                msg_type = "text"

            # title / filename
            title = None
            if msg.file:
                title = getattr(msg.file, "name", None)

            file_size = getattr(msg.file, "size", None) if msg.file else None
            duration = getattr(msg.file, "duration", None) if msg.file else None
            mime_type = getattr(msg.file, "mime_type", None) if msg.file else None
            file_unique_id = getattr(msg.file, "unique_id", None) if msg.file else None

            # caption only if media exists
            caption = text if msg.media else None
            message_text = text

            cur.execute("""
                INSERT INTO messages (
                    message_id,
                    channel_id,
                    type,
                    title,
                    message_text,
                    caption,
                    file_size,
                    duration,
                    mime_type,
                    file_unique_id,
                    created_at,
                    updated_at
                )
                VALUES (
                    %s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,NOW()
                )
                ON DUPLICATE KEY UPDATE
                    type=VALUES(type),
                    title=VALUES(title),
                    message_text=VALUES(message_text),
                    caption=VALUES(caption),
                    file_size=VALUES(file_size),
                    duration=VALUES(duration),
                    mime_type=VALUES(mime_type),
                    file_unique_id=VALUES(file_unique_id),
                    updated_at=NOW()
            """, (
                msg.id,
                channel_id,
                msg_type,
                title,
                message_text,
                caption,
                file_size,
                duration,
                mime_type,
                file_unique_id,
                msg.date
            ))

    finally:
        conn.close()


def delete_messages(message_ids, channel_id):
    if not message_ids:
        return

    conn = get_connection()
    try:
        with conn.cursor() as cur:
            sql = """
                DELETE FROM messages
                WHERE channel_id = %s
                AND message_id IN ({})
            """.format(",".join(["%s"] * len(message_ids)))

            params = [channel_id] + list(message_ids)
            cur.execute(sql, params)
    finally:
        conn.close()


# =========================================================
# KEEP ALIVE
# =========================================================
async def keep_alive():
    while True:
        try:
            await client(functions.PingRequest(ping_id=0))
            print("💓 ping")
        except Exception as e:
            print("Ping error:", e)

        await asyncio.sleep(30)


# =========================================================
# INITIAL SYNC
# =========================================================
async def sync_old_messages():
    entity = await client.get_entity(CHANNEL)
    channel_id = entity.id

    while True:
        try:
            last_id = get_last_message_id()
            print("Resume from:", last_id)

            messages = await client.get_messages(
                entity,
                limit=BATCH_SIZE,
                min_id=last_id,
                reverse=True
            )

            if not messages:
                print("✅ Initial sync completed.")
                break

            for msg in messages:
                try:
                    save_message(msg, channel_id)
                    print("Saved:", msg.id)
                except Exception as e:
                    print("Save error:", e)

            await asyncio.sleep(SLEEP_BETWEEN_BATCH)

        except FloodWaitError as e:
            print("FloodWait:", e.seconds)
            await asyncio.sleep(e.seconds)

        except RPCError as e:
            print("RPCError:", e)
            await asyncio.sleep(5)

        except Exception as e:
            print("Sync error:", e)
            await asyncio.sleep(5)


# =========================================================
# LIVE HANDLERS
# =========================================================
def register_handlers():
    @client.on(events.NewMessage(chats=CHANNEL))
    async def on_new_message(event):
        try:
            entity = await client.get_entity(CHANNEL)
            save_message(event.message, entity.id)
            print("➕ New:", event.message.id)
        except Exception as e:
            print("New message error:", e)

    @client.on(events.MessageEdited(chats=CHANNEL))
    async def on_message_edited(event):
        try:
            entity = await client.get_entity(CHANNEL)
            save_message(event.message, entity.id)
            print("✏️ Edited:", event.message.id)
        except Exception as e:
            print("Edit error:", e)

    @client.on(events.MessageDeleted(chats=CHANNEL))
    async def on_message_deleted(event):
        try:
            entity = await client.get_entity(CHANNEL)
            delete_messages(event.deleted_ids, entity.id)
            print("🗑 Deleted:", event.deleted_ids)
        except Exception as e:
            print("Delete error:", e)


# =========================================================
# MAIN
# =========================================================
async def main():
    await client.start()
    print("Connected to Telegram.")

    register_handlers()

    # keep alive task
    asyncio.create_task(keep_alive())

    # sync historical messages
    await sync_old_messages()

    print("Listening for new updates...")
    await client.run_until_disconnected()


# =========================================================
# ENTRY POINT
# IMPORTANT: DO NOT USE asyncio.run(main())
# =========================================================
if __name__ == "__main__":
    print("Starting...")
    with client:
        client.loop.run_until_complete(main())