from datetime import UTC, datetime, timedelta

import structlog
from sqlalchemy import and_, select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload

from app.database.models import CryptoBotPayment


logger = structlog.get_logger(__name__)


async def create_cryptobot_payment(
    db: AsyncSession,
    user_id: int,
    invoice_id: str,
    amount: str,
    asset: str,
    status: str = 'active',
    description: str | None = None,
    payload: str | None = None,
    bot_invoice_url: str | None = None,
    mini_app_invoice_url: str | None = None,
    web_app_invoice_url: str | None = None,
) -> CryptoBotPayment:
    payment = CryptoBotPayment(
        user_id=user_id,
        invoice_id=invoice_id,
        amount=amount,
        asset=asset,
        status=status,
        description=description,
        payload=payload,
        bot_invoice_url=bot_invoice_url,
        mini_app_invoice_url=mini_app_invoice_url,
        web_app_invoice_url=web_app_invoice_url,
    )

    db.add(payment)
    await db.commit()
    await db.refresh(payment)

    logger.info(
        'Создан CryptoBot платеж: на для пользователя',
        invoice_id=invoice_id,
        amount=amount,
        asset=asset,
        user_id=user_id,
    )
    return payment


async def get_cryptobot_payment_by_invoice_id(db: AsyncSession, invoice_id: str) -> CryptoBotPayment | None:
    result = await db.execute(
        select(CryptoBotPayment)
        .options(selectinload(CryptoBotPayment.user))
        .where(CryptoBotPayment.invoice_id == invoice_id)
    )
    return result.scalar_one_or_none()


async def get_cryptobot_payment_by_id(db: AsyncSession, payment_id: int) -> CryptoBotPayment | None:
    result = await db.execute(
        select(CryptoBotPayment).options(selectinload(CryptoBotPayment.user)).where(CryptoBotPayment.id == payment_id)
    )
    return result.scalar_one_or_none()


async def update_cryptobot_payment_status(
    db: AsyncSession, invoice_id: str, status: str, paid_at: datetime | None = None
) -> CryptoBotPayment | None:
    payment = await get_cryptobot_payment_by_invoice_id(db, invoice_id)

    if not payment:
        return None

    payment.status = status
    payment.updated_at = datetime.now(UTC)

    if status == 'paid' and paid_at:
        payment.paid_at = paid_at

    await db.commit()
    await db.refresh(payment)

    logger.info('Обновлен статус CryptoBot платежа', invoice_id=invoice_id, status=status)
    return payment


async def link_cryptobot_payment_to_transaction(
    db: AsyncSession, invoice_id: str, transaction_id: int
) -> CryptoBotPayment | None:
    payment = await get_cryptobot_payment_by_invoice_id(db, invoice_id)

    if not payment:
        return None

    payment.transaction_id = transaction_id
    payment.updated_at = datetime.now(UTC)

    await db.commit()
    await db.refresh(payment)

    logger.info('Связан CryptoBot платеж с транзакцией', invoice_id=invoice_id, transaction_id=transaction_id)
    return payment


async def get_user_cryptobot_payments(
    db: AsyncSession, user_id: int, limit: int = 50, offset: int = 0
) -> list[CryptoBotPayment]:
    result = await db.execute(
        select(CryptoBotPayment)
        .where(CryptoBotPayment.user_id == user_id)
        .order_by(CryptoBotPayment.created_at.desc())
        .offset(offset)
        .limit(limit)
    )
    return result.scalars().all()


async def get_pending_cryptobot_payments(db: AsyncSession, older_than_hours: int = 24) -> list[CryptoBotPayment]:
    cutoff_time = datetime.now(UTC) - timedelta(hours=older_than_hours)

    result = await db.execute(
        select(CryptoBotPayment)
        .options(selectinload(CryptoBotPayment.user))
        .where(and_(CryptoBotPayment.status == 'active', CryptoBotPayment.created_at < cutoff_time))
        .order_by(CryptoBotPayment.created_at)
    )
    return result.scalars().all()
