"""Сервис для обработки заявок на партнёрский статус."""

from datetime import UTC, datetime

import structlog
from sqlalchemy import desc, func, select, update
from sqlalchemy.ext.asyncio import AsyncSession

from app.database.models import AdvertisingCampaign, PartnerApplication, PartnerStatus, User
from app.utils.user_utils import generate_unique_referral_code


logger = structlog.get_logger(__name__)


class PartnerApplicationService:
    """Сервис управления партнёрскими заявками."""

    async def submit_application(
        self,
        db: AsyncSession,
        user_id: int,
        company_name: str | None = None,
        website_url: str | None = None,
        telegram_channel: str | None = None,
        description: str | None = None,
        expected_monthly_referrals: int | None = None,
        desired_commission_percent: int | None = None,
    ) -> tuple[PartnerApplication | None, str]:
        """
        Подаёт заявку на партнёрский статус.
        Возвращает (application, error_message).
        """
        user = await db.get(User, user_id)
        if not user:
            return None, 'Пользователь не найден'

        if user.partner_status == PartnerStatus.APPROVED.value:
            return None, 'Вы уже являетесь партнёром'

        if user.partner_status == PartnerStatus.PENDING.value:
            return None, 'У вас уже есть заявка на рассмотрении'

        application = PartnerApplication(
            user_id=user_id,
            company_name=company_name,
            website_url=website_url,
            telegram_channel=telegram_channel,
            description=description,
            expected_monthly_referrals=expected_monthly_referrals,
            desired_commission_percent=desired_commission_percent,
        )

        user.partner_status = PartnerStatus.PENDING.value

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

        logger.info(
            '📝 Подана заявка на партнёрство',
            user_id=user_id,
            application_id=application.id,
        )

        return application, ''

    async def approve_application(
        self,
        db: AsyncSession,
        application_id: int,
        admin_id: int,
        commission_percent: int,
        comment: str | None = None,
    ) -> tuple[bool, str]:
        """
        Одобряет заявку на партнёрство.
        Возвращает (success, error_message).
        """
        result = await db.execute(
            select(PartnerApplication).where(PartnerApplication.id == application_id).with_for_update()
        )
        application = result.scalar_one_or_none()
        if not application:
            return False, 'Заявка не найдена'

        if application.status != PartnerStatus.PENDING.value:
            return False, 'Заявка уже обработана'

        user_result = await db.execute(select(User).where(User.id == application.user_id).with_for_update())
        user = user_result.scalar_one_or_none()
        if not user:
            return False, 'Пользователь не найден'

        # Генерируем реферальный код, если его нет
        if not user.referral_code:
            user.referral_code = await generate_unique_referral_code(db, user.telegram_id or 0)

        user.partner_status = PartnerStatus.APPROVED.value
        user.referral_commission_percent = commission_percent

        application.status = PartnerStatus.APPROVED.value
        application.approved_commission_percent = commission_percent
        application.admin_comment = comment
        application.processed_by = admin_id
        application.processed_at = datetime.now(UTC)

        await db.commit()

        logger.info(
            '✅ Партнёрская заявка одобрена',
            application_id=application_id,
            user_id=application.user_id,
            commission_percent=commission_percent,
            admin_id=admin_id,
        )

        return True, ''

    async def reject_application(
        self,
        db: AsyncSession,
        application_id: int,
        admin_id: int,
        comment: str | None = None,
    ) -> tuple[bool, str]:
        """Отклоняет заявку на партнёрство."""
        result = await db.execute(
            select(PartnerApplication).where(PartnerApplication.id == application_id).with_for_update()
        )
        application = result.scalar_one_or_none()
        if not application:
            return False, 'Заявка не найдена'

        if application.status != PartnerStatus.PENDING.value:
            return False, 'Заявка уже обработана'

        user_result = await db.execute(select(User).where(User.id == application.user_id).with_for_update())
        user = user_result.scalar_one_or_none()
        if user:
            user.partner_status = PartnerStatus.REJECTED.value

        application.status = PartnerStatus.REJECTED.value
        application.admin_comment = comment
        application.processed_by = admin_id
        application.processed_at = datetime.now(UTC)

        await db.commit()

        logger.info(
            '❌ Партнёрская заявка отклонена',
            application_id=application_id,
            user_id=application.user_id,
            admin_id=admin_id,
        )

        return True, ''

    async def revoke_partner(
        self,
        db: AsyncSession,
        user_id: int,
        admin_id: int,
    ) -> tuple[bool, str]:
        """Отзывает партнёрский статус."""
        user = await db.get(User, user_id)
        if not user:
            return False, 'Пользователь не найден'

        if user.partner_status != PartnerStatus.APPROVED.value:
            return False, 'Пользователь не является партнёром'

        user.partner_status = PartnerStatus.NONE.value
        user.referral_commission_percent = None

        # Отвязываем все кампании от бывшего партнёра
        await db.execute(
            update(AdvertisingCampaign)
            .where(AdvertisingCampaign.partner_user_id == user_id)
            .values(partner_user_id=None)
        )

        await db.commit()

        logger.info(
            '🚫 Партнёрский статус отозван',
            user_id=user_id,
            admin_id=admin_id,
        )

        return True, ''

    async def get_pending_applications(self, db: AsyncSession) -> list[PartnerApplication]:
        """Получает все заявки на рассмотрении."""
        result = await db.execute(
            select(PartnerApplication)
            .where(PartnerApplication.status == PartnerStatus.PENDING.value)
            .order_by(PartnerApplication.created_at.asc())
        )
        return list(result.scalars().all())

    async def get_all_applications(
        self,
        db: AsyncSession,
        status: str | None = None,
        limit: int = 50,
        offset: int = 0,
    ) -> tuple[list[PartnerApplication], int]:
        """Получает заявки с фильтрацией. Возвращает (items, total)."""
        query = select(PartnerApplication)
        count_query = select(func.count()).select_from(PartnerApplication)

        if status:
            query = query.where(PartnerApplication.status == status)
            count_query = count_query.where(PartnerApplication.status == status)

        total_result = await db.execute(count_query)
        total = total_result.scalar() or 0

        query = query.order_by(desc(PartnerApplication.created_at)).offset(offset).limit(limit)
        result = await db.execute(query)

        return list(result.scalars().all()), total

    async def get_latest_application(self, db: AsyncSession, user_id: int) -> PartnerApplication | None:
        """Получает последнюю заявку пользователя."""
        result = await db.execute(
            select(PartnerApplication)
            .where(PartnerApplication.user_id == user_id)
            .order_by(desc(PartnerApplication.created_at))
            .limit(1)
        )
        return result.scalar_one_or_none()


# Синглтон сервиса
partner_application_service = PartnerApplicationService()
