#!/usr/bin/env python3

import sys
import re
import asyncio
import getpass
from pathlib import Path

import pymysql
from telethon import TelegramClient
from telethon.errors import (
    SessionPasswordNeededError,
    PhoneCodeInvalidError,
    PhoneCodeExpiredError,
)

# ---------------------------------------------------------
# Paths
# ---------------------------------------------------------

BASE_DIR = Path(__file__).resolve().parent.parent
DB_CONFIG = BASE_DIR / "config" / "database.php"
SESSION_DIR = BASE_DIR / "engine" / "sessions"

SESSION_DIR.mkdir(parents=True, exist_ok=True)


# ---------------------------------------------------------
# Helpers
# ---------------------------------------------------------

def fail(message):
    print("\n❌ ERROR")
    print(message)
    sys.exit(1)


def read_php_database_config():
    if not DB_CONFIG.exists():
        fail(f"Database config not found: {DB_CONFIG}")

    content = DB_CONFIG.read_text(
        encoding="utf-8",
        errors="ignore"
    )

    patterns = {
        "host": r"\$dbHost\s*=\s*['\"]([^'\"]+)['\"]",
        "name": r"\$dbName\s*=\s*['\"]([^'\"]+)['\"]",
        "user": r"\$dbUser\s*=\s*['\"]([^'\"]+)['\"]",
        "password": r"\$dbPass\s*=\s*['\"]([^'\"]*)['\"]",
    }

    result = {}

    for key, pattern in patterns.items():
        match = re.search(pattern, content)

        if not match:
            fail(
                f"Could not read '{key}' "
                f"from config/database.php"
            )

        result[key] = match.group(1)

    return result


def get_database():
    cfg = read_php_database_config()

    try:
        return pymysql.connect(
            host=cfg["host"],
            user=cfg["user"],
            password=cfg["password"],
            database=cfg["name"],
            charset="utf8mb4",
            cursorclass=pymysql.cursors.DictCursor,
            autocommit=True,
        )

    except Exception as exc:
        fail(f"Database connection failed: {exc}")


def update_publisher(
    db,
    publisher_id,
    health_status,
    last_error=None
):
    with db.cursor() as cursor:
        cursor.execute(
            """
            UPDATE publishers
            SET
                health_status = %s,
                last_error = %s,
                last_activity_at = NOW()
            WHERE id = %s
            """,
            (
                health_status,
                last_error,
                publisher_id,
            )
        )


# ---------------------------------------------------------
# Main
# ---------------------------------------------------------

async def main():

    if len(sys.argv) < 2:
        fail(
            "Publisher ID required.\n"
            "Example:\n"
            "python3 engine/publisher_login.py 1"
        )

    try:
        publisher_id = int(sys.argv[1])
    except ValueError:
        fail("Publisher ID must be numeric.")

    db = get_database()

    try:

        with db.cursor() as cursor:
            cursor.execute(
                """
                SELECT *
                FROM publishers
                WHERE id = %s
                LIMIT 1
                """,
                (publisher_id,)
            )

            publisher = cursor.fetchone()

        if not publisher:
            fail(
                f"Publisher #{publisher_id} not found."
            )

        print("")
        print("==============================================")
        print("TELEGRAM PUBLISHER LOGIN")
        print("==============================================")
        print(f"Publisher ID:   {publisher['id']}")
        print(f"Name:           {publisher['name']}")
        print(f"Type:           {publisher['publisher_type']}")
        print(f"Active:         {publisher['is_active']}")
        print("----------------------------------------------")

        if publisher["publisher_type"] != "telegram_user":
            fail(
                "This login script is only for "
                "telegram_user publishers."
            )

        if int(publisher["is_active"]) != 1:
            fail("Publisher is inactive.")

        phone = str(
            publisher.get("telegram_phone") or ""
        ).strip()

        api_id_raw = str(
            publisher.get("telegram_api_id") or ""
        ).strip()

        api_hash = str(
            publisher.get("telegram_api_hash") or ""
        ).strip()

        session_name = str(
            publisher.get("session_name") or ""
        ).strip()

        if not phone:
            fail(
                "Telegram Phone is missing "
                "in Publishers Back Office."
            )

        if not api_id_raw:
            fail(
                "Telegram API ID is missing "
                "in Publishers Back Office."
            )

        try:
            api_id = int(api_id_raw)
        except ValueError:
            fail("Telegram API ID must be numeric.")

        if not api_hash:
            fail(
                "Telegram API Hash is missing "
                "in Publishers Back Office."
            )

        if not session_name:
            session_name = f"publisher_{publisher_id}"

        # Prevent unsafe path characters
        session_name = re.sub(
            r"[^a-zA-Z0-9_-]",
            "_",
            session_name
        )

        session_path = SESSION_DIR / session_name

        print(f"Phone:          {phone}")
        print(f"Session:        {session_name}")
        print("==============================================")
        print("")

        client = TelegramClient(
            str(session_path),
            api_id,
            api_hash
        )

        try:

            await client.connect()

            # -------------------------------------------------
            # Existing session
            # -------------------------------------------------

            if await client.is_user_authorized():

                me = await client.get_me()

                update_publisher(
                    db,
                    publisher_id,
                    "healthy",
                    None
                )

                print("✅ Existing Telegram session is valid.")
                print("")
                print("----------------------------------------------")
                print(f"Telegram ID:    {me.id}")
                print(
                    f"Username:       "
                    f"@{me.username if me.username else '-'}"
                )
                print(
                    f"Name:           "
                    f"{me.first_name or ''} "
                    f"{me.last_name or ''}"
                )
                print("----------------------------------------------")
                print("PUBLISHER LOGIN SUCCESS ✅")
                print("")

                return

            # -------------------------------------------------
            # Send login code
            # -------------------------------------------------

            print("Sending Telegram login code...")

            sent = await client.send_code_request(phone)

            print("✅ Login code sent.")
            print("")

            code = input(
                "Enter Telegram login code: "
            ).strip().replace(" ", "")

            if not code:
                fail("Login code cannot be empty.")

            try:

                await client.sign_in(
                    phone=phone,
                    code=code,
                    phone_code_hash=sent.phone_code_hash
                )

            except SessionPasswordNeededError:

                print("")
                print("Telegram 2FA is enabled.")

                password = getpass.getpass(
                    "Enter Telegram 2FA password: "
                )

                await client.sign_in(
                    password=password
                )

            except PhoneCodeInvalidError:
                fail("Telegram login code is invalid.")

            except PhoneCodeExpiredError:
                fail(
                    "Telegram login code expired. "
                    "Run the script again."
                )

            # -------------------------------------------------
            # Verify
            # -------------------------------------------------

            if not await client.is_user_authorized():
                fail(
                    "Telegram authorization failed."
                )

            me = await client.get_me()

            update_publisher(
                db,
                publisher_id,
                "healthy",
                None
            )

            print("")
            print("==============================================")
            print("PUBLISHER LOGIN SUCCESS ✅")
            print("==============================================")
            print(f"Telegram ID:    {me.id}")
            print(
                f"Username:       "
                f"@{me.username if me.username else '-'}"
            )
            print(
                f"Name:           "
                f"{me.first_name or ''} "
                f"{me.last_name or ''}"
            )
            print(f"Session:        {session_name}.session")
            print("Health:         healthy")
            print("==============================================")
            print("")

        except Exception as exc:

            try:
                update_publisher(
                    db,
                    publisher_id,
                    "error",
                    str(exc)[:2000]
                )
            except Exception:
                pass

            raise

        finally:

            if client.is_connected():
                await client.disconnect()

    finally:

        db.close()


if __name__ == "__main__":

    try:
        asyncio.run(main())

    except KeyboardInterrupt:
        print("\nCancelled.")

    except Exception as exc:
        print("")
        print("❌ LOGIN FAILED")
        print(str(exc))
        sys.exit(1)
