"""
This example shows how to use webhook on behind of any reverse proxy (nginx, traefik, ingress etc.)
"""

from __future__ import annotations

import logging
import sys
from os import getenv
from typing import TYPE_CHECKING, Any

import uvicorn
from litestar import Litestar, Request, post
from litestar.di import Provide

from litegram import Bot, Dispatcher, Router
from litegram.client.default import DefaultBotProperties
from litegram.enums import ParseMode
from litegram.filters import CommandStart
from litegram.types import Update
from litegram.utils.markdown import hbold
from litegram.webhook.litestar_server import webhook_handler

if TYPE_CHECKING:
    from litegram.types import Message

# Bot token can be obtained via https://t.me/BotFather
TOKEN = getenv("BOT_TOKEN")

# Webserver settings
# bind localhost only to prevent any external access
WEB_SERVER_HOST = "127.0.0.1"
# Port for incoming request from reverse proxy. Should be any available port
WEB_SERVER_PORT = 8080

# Path to webhook route, on which Telegram will send requests
WEBHOOK_PATH = "/webhook"
# Secret key to validate requests from Telegram (optional)
WEBHOOK_SECRET = "my-secret"
# Base URL for webhook will be used to generate webhook URL for Telegram,
# in this example it is used public DNS with HTTPS support
BASE_WEBHOOK_URL = "https://litegram.dev"

# All handlers should be attached to the Router (or Dispatcher)
router = Router()


@router.message(CommandStart())
async def command_start_handler(message: Message) -> None:
    """
    This handler receives messages with `/start` command
    """
    # Most event objects have aliases for API methods that can be called in events' context
    # For example if you want to answer to incoming message you can use `message.answer(...)` alias
    # and the target chat will be passed to :ref:`litegram.methods.send_message.SendMessage`
    # method automatically or call API method directly via
    # Bot instance: `bot.send_message(chat_id=message.chat.id, ...)`
    await message.answer(f"Hello, {hbold(message.from_user.full_name)}!")


@router.message()
async def echo_handler(message: Message) -> None:
    """
    Handler will forward receive a message back to the sender

    By default, message handler will handle all message types (like text, photo, sticker etc.)
    """
    try:
        # Send a copy of the received message
        await message.send_copy(chat_id=message.chat.id)
    except TypeError:
        # But not all the types is supported to be copied so need to handle it
        await message.answer("Nice try!")


async def on_startup(bot: Bot) -> None:
    # If you have a self-signed SSL certificate, then you will need to send a public
    # certificate to Telegram
    await bot.set_webhook(f"{BASE_WEBHOOK_URL}{WEBHOOK_PATH}", secret_token=WEBHOOK_SECRET)


def main() -> None:
    # Dispatcher is a root router
    dp = Dispatcher()
    # ... and all other routers should be attached to Dispatcher
    dp.include_router(router)

    # Register startup hook to initialize webhook
    dp.startup.register(on_startup)

    # Initialize Bot instance with default bot properties which will be passed to all API calls
    bot = Bot(token=TOKEN, default=DefaultBotProperties(parse_mode=ParseMode.HTML))

    # And finally start webserver
    app = Litestar(
        route_handlers=[post(WEBHOOK_PATH)(webhook_handler)],
        dependencies={
            "bot": Provide(lambda: bot),
            "dispatcher": Provide(lambda: dp),
            "secret_token": Provide(lambda: WEBHOOK_SECRET),
        },
        on_startup=[lambda: dp.emit_startup(bot=bot)],
        on_shutdown=[lambda: dp.emit_shutdown(bot=bot), bot.session.close],
    )

    uvicorn.run(app, host=WEB_SERVER_HOST, port=WEB_SERVER_PORT)


if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO, stream=sys.stdout)
    main()
