"""API views for Telegram-only authentication."""

from __future__ import annotations

import logging

from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import OpenApiResponse, extend_schema
from rest_framework import status, viewsets
from rest_framework.decorators import api_view, permission_classes, action
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework_simplejwt.tokens import RefreshToken
from django.conf import settings
from django.views.decorators.csrf import csrf_exempt

from utils.telegram import (
    TelegramDataValidationError,
    init_data_debug_snapshot,
    send_telegram_message,
    validate_telegram_init_data,
)
from .models import User
from .serializers import (
    TelegramAuthRequestSerializer,
    TelegramAuthResponseSerializer,
    TelegramAuthenticatedUserSerializer,
    TelegramWebhookResponseSerializer,
    UserWalletSerializer,
)


logger = logging.getLogger(__name__)


def _authenticate_with_init_data(init_data: str, start_param: str | None):
    """Shared logic for authenticating a user from an initData payload."""
    try:
        user_data = validate_telegram_init_data(
            init_data,
            settings.TELEGRAM_BOT_TOKEN,
        )
    except TelegramDataValidationError as exc:
        return Response(
            {"error": str(exc)},
            status=status.HTTP_401_UNAUTHORIZED,
        )

    telegram_user_id = user_data.get("id")
    if not telegram_user_id:
        return Response(
            {"error": "Invalid user data"},
            status=status.HTTP_400_BAD_REQUEST,
        )

    defaults = {
        "username": user_data.get("username"),
        "first_name": user_data.get("first_name", ""),
        "last_name": user_data.get("last_name"),
        "language_code": user_data.get("language_code"),
        "photo_url": user_data.get("photo_url"),
    }

    user, created = User.objects.get_or_create(
        telegram_user_id=telegram_user_id,
        defaults=defaults,
    )

    # If user already exists, refresh profile details that might change on Telegram.
    if not created:
        changed_fields: list[str] = []
        for field, value in defaults.items():
            if value is not None and getattr(user, field) != value:
                setattr(user, field, value)
                changed_fields.append(field)
        if changed_fields:
            user.save(update_fields=changed_fields)

    # Handle referral assignment if provided and not already set.
    if start_param and not user.referred_by_id:
        try:
            referrer = User.objects.get(referral_code=start_param)
            if referrer.id != user.id:
                user.referred_by = referrer
                user.save(update_fields=["referred_by"])
        except User.DoesNotExist:
            pass

    refresh = RefreshToken.for_user(user)

    return Response(
        {
            "access": str(refresh.access_token),
            "refresh": str(refresh),
            "user": {
                "id": user.id,
                "telegram_user_id": user.telegram_user_id,
                "username": user.username,
                "first_name": user.first_name,
                "last_name": user.last_name,
                "language_code": user.language_code,
                "photo_url": user.photo_url,
                "credit_balance": str(user.credit_balance),
                "bonus_balance": str(user.bonus_balance),
                "referral_code": user.referral_code,
                "referred_by": user.referred_by_id,
            },
        }
    )


@extend_schema(
    request=TelegramAuthRequestSerializer,
    responses={
        200: TelegramAuthResponseSerializer,
        400: OpenApiResponse(description="Missing or invalid init_data/start_param"),
        401: OpenApiResponse(description="Telegram signature validation failed"),
    },
    description="Authenticate a user via Telegram WebApp initData and issue JWTs.",
)
@api_view(["POST"])
@permission_classes([AllowAny])
def authenticate_telegram(request):
    """
    Authenticate a user using Telegram Web App initData and return JWTs.
    """
    init_data = request.data.get("init_data")
    start_param = request.data.get("start_param")

    if not init_data:
        return Response(
            {"error": "init_data is required"},
            status=status.HTTP_400_BAD_REQUEST,
        )

    return _authenticate_with_init_data(init_data, start_param)


@extend_schema(
    request=TelegramAuthRequestSerializer,
    responses={
        200: TelegramAuthResponseSerializer,
        400: OpenApiResponse(description="Missing init_data"),
        401: OpenApiResponse(description="Signature validation failed"),
        404: OpenApiResponse(description="Dev auth disabled"),
    },
    description=(
        "Development-only Telegram auth helper. Validates a signed initData "
        "payload and returns JWTs plus signature debug info. Only available "
        "when DEBUG=True and ENABLE_DEV_TELEGRAM_AUTH=True."
    ),
)
@api_view(["POST"])
@permission_classes([AllowAny])
def authenticate_telegram_dev(request):
    """
    Dev-only helper endpoint to test Telegram auth flows without the Mini App.
    """
    if not (settings.DEBUG and getattr(settings, "ENABLE_DEV_TELEGRAM_AUTH", False)):
        return Response(status=status.HTTP_404_NOT_FOUND)

    init_data = request.data.get("init_data")
    start_param = request.data.get("start_param")

    if not init_data:
        return Response(
            {"error": "init_data is required"},
            status=status.HTTP_400_BAD_REQUEST,
        )

    debug_info = init_data_debug_snapshot(
        init_data,
        settings.TELEGRAM_BOT_TOKEN,
    )
    logger.info(
        "Dev Telegram auth debug: provided_hash=%s calculated_hash=%s",
        debug_info.get("provided_hash"),
        debug_info.get("calculated_hash"),
    )

    response = _authenticate_with_init_data(init_data, start_param)
    if response.status_code == status.HTTP_200_OK:
        payload = dict(response.data)
        payload["debug"] = debug_info
        response.data = payload

    return response


@extend_schema(
    request=OpenApiTypes.OBJECT,
    responses={
        200: TelegramAuthResponseSerializer,
        400: OpenApiResponse(description="Missing username"),
        404: OpenApiResponse(description="User not found or Dev auth disabled"),
    },
    description=(
        "Development-only helper to switch the authenticated user by username. "
        "Only available when DEBUG=True and ENABLE_DEV_TELEGRAM_AUTH=True."
    ),
)
@api_view(["POST"])
@permission_classes([AllowAny])
def switch_user_dev(request):
    """
    Dev-only helper to switch user context by username.
    """
    if not (settings.DEBUG and getattr(settings, "ENABLE_DEV_TELEGRAM_AUTH", False)):
        return Response(status=status.HTTP_404_NOT_FOUND)

    username = request.data.get("username")
    if not username:
        return Response(
            {"error": "username is required"},
            status=status.HTTP_400_BAD_REQUEST,
        )

    try:
        user = User.objects.get(username=username)
    except User.DoesNotExist:
        return Response(
            {"error": f"User with username '{username}' not found"},
            status=status.HTTP_404_NOT_FOUND,
        )

    refresh = RefreshToken.for_user(user)

    return Response(
        {
            "access": str(refresh.access_token),
            "refresh": str(refresh),
            "user": {
                "id": user.id,
                "telegram_user_id": user.telegram_user_id,
                "username": user.username,
                "first_name": user.first_name,
                "last_name": user.last_name,
                "language_code": user.language_code,
                "photo_url": user.photo_url,
                "credit_balance": str(user.credit_balance),
                "bonus_balance": str(user.bonus_balance),
                "referral_code": user.referral_code,
                "referred_by": user.referred_by_id,
            },
        }
    )


class MeViewSet(viewsets.ViewSet):
    """Endpoints for current user profile and wallets."""

    @extend_schema(
        responses={200: TelegramAuthenticatedUserSerializer},
        description="Retrieve the authenticated user's profile.",
    )
    def list(self, request):
        """GET /api/users/me/ returns the current user profile."""
        serializer = TelegramAuthenticatedUserSerializer(request.user)
        return Response(serializer.data)

    @extend_schema(
        responses={200: UserWalletSerializer(many=True)},
        description="Retrieve all wallet balances for the authenticated user.",
    )
    @action(detail=False, methods=["get"])
    def wallets(self, request):
        """GET /api/users/me/wallets/ returns all wallet balances."""
        user = request.user
        wallets = [
            {"name": "Primary", "type": "credit_balance", "balance": user.credit_balance},
            {"name": "Bonus", "type": "bonus", "balance": user.bonus_balance},
        ]
        serializer = UserWalletSerializer(wallets, many=True)
        return Response(serializer.data)


@csrf_exempt
@extend_schema(
    request=OpenApiTypes.OBJECT,
    responses={
        200: TelegramWebhookResponseSerializer,
        403: TelegramWebhookResponseSerializer,
    },
    description=(
        "Webhook for Telegram bot updates. Returns ok/forbidden depending on the "
        "configured webhook secret."
    ),
)
@api_view(["POST"])
@permission_classes([AllowAny])
def telegram_webhook(request):
    """
    Receive Telegram webhook updates and handle /start deep links for referrals.
    """
    secret_token = settings.TELEGRAM_WEBHOOK_SECRET
    header_secret = request.headers.get("X-Telegram-Bot-Api-Secret-Token")
    if secret_token and header_secret != secret_token:
        return Response({"ok": False, "error": "forbidden"}, status=403)

    update = request.data or {}
    message = update.get("message") or update.get("edited_message")
    callback_query = update.get("callback_query")

    # Extract chat/message context.
    chat = None
    text = ""
    if message:
        chat = message.get("chat")
        text = message.get("text", "") or ""
    elif callback_query:
        message = callback_query.get("message") or {}
        chat = message.get("chat")
        text = callback_query.get("data", "") or ""

    if not chat or "id" not in chat:
        return Response({"ok": True})  # Nothing to do; ack to prevent retries.

    chat_id = chat["id"]
    start_param: str | None = None
    if text.startswith("/start"):
        parts = text.split(maxsplit=1)
        start_param = parts[1].strip() if len(parts) > 1 else None
    elif callback_query and callback_query.get("data"):
        start_param = callback_query["data"]

    web_app_url = getattr(settings, "TELEGRAM_WEBAPP_URL", "")
    button_url = web_app_url
    if web_app_url and start_param:
        separator = "&" if "?" in web_app_url else "?"
        button_url = f"{web_app_url}{separator}startapp={start_param}"

    # Prepare reply content.
    if web_app_url:
        reply_markup = {
            "keyboard": [
                [
                    {
                        "text": "Open app",
                        "web_app": {"url": button_url},
                    }
                ]
            ],
            "resize_keyboard": True,
            "one_time_keyboard": False,
        }
        reply_text = (
            "Tap the button below to open the app. "
            "Referral code is passed automatically."
        )
    else:
        reply_markup = None
        reply_text = "App URL is not configured. Please try again later."

    sent = send_telegram_message(
        bot_token=settings.TELEGRAM_BOT_TOKEN,
        chat_id=chat_id,
        text=reply_text,
        reply_markup=reply_markup,
    )

    if not sent:
        logger.warning("Failed to reply to Telegram webhook for chat_id=%s", chat_id)

    return Response({"ok": True})
