from datetime import datetime, timedelta
from typing import List
from uuid import UUID
import logging

from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from pydantic import BaseModel

from ..models import Account
from ..models import UsageEvent
from ..notifications import send_email, send_webhook

log = logging.getLogger(__name__)


class AlertThreshold(BaseModel):
    """Alert configuration"""

    low_balance: int = 100
    critical_balance: int = 20
    usage_spike_multiplier: float = 2.0  # Alert if usage is 2x normal


class CreditAlertService:
    """Manages credit balance alerts and notifications"""

    def __init__(self, db: AsyncSession):
        self.db = db
        self.thresholds = AlertThreshold()

    async def check_low_balance_alerts(self) -> List[dict]:
        """Check all accounts for low balance conditions"""
        alerts_sent = []

        # Find accounts with low balance
        stmt = select(Account).where(
            Account.credits_balance < self.thresholds.low_balance
        )
        result = await self.db.execute(stmt)
        accounts = result.scalars().all()

        for account in accounts:
            alert_type = (
                "critical"
                if account.credits_balance < self.thresholds.critical_balance
                else "low"
            )

            # Check if we already sent an alert recently
            if not await self._should_send_alert(account.id, alert_type):
                continue

            # Send alert
            await self._send_balance_alert(account, alert_type)
            alerts_sent.append(
                {
                    "account_id": str(account.id),
                    "type": alert_type,
                    "balance": account.credits_balance,
                }
            )

            # Record alert
            await self._record_alert(account.id, alert_type, account.credits_balance)

        return alerts_sent

    async def check_usage_spike_alerts(self) -> List[dict]:
        """Check for unusual usage spikes"""
        alerts_sent = []

        # Get average daily usage for last 30 days
        thirty_days_ago = datetime.utcnow() - timedelta(days=30)

        query = """
        WITH daily_usage AS (
            SELECT
                account_id,
                DATE(time) as usage_date,
                SUM(credits_consumed) as daily_credits
            FROM usage_events
            WHERE time > :start_date
            GROUP BY account_id, DATE(time)
        ),
        usage_stats AS (
            SELECT
                account_id,
                AVG(daily_credits) as avg_daily_usage,
                MAX(daily_credits) as max_daily_usage
            FROM daily_usage
            GROUP BY account_id
        )
        SELECT
            u.account_id,
            u.avg_daily_usage,
            TODAY.daily_credits as today_usage
        FROM usage_stats u
        JOIN (
            SELECT
                account_id,
                SUM(credits_consumed) as daily_credits
            FROM usage_events
            WHERE DATE(time) = CURRENT_DATE
            GROUP BY account_id
        ) TODAY ON u.account_id = TODAY.account_id
        WHERE TODAY.daily_credits > u.avg_daily_usage * :spike_multiplier
        """

        result = await self.db.execute(
            query,
            {
                "start_date": thirty_days_ago,
                "spike_multiplier": self.thresholds.usage_spike_multiplier,
            },
        )

        for row in result:
            account = await self.db.get(Account, row.account_id)
            if account:
                await self._send_usage_spike_alert(
                    account, row.today_usage, row.avg_daily_usage
                )
                alerts_sent.append(
                    {
                        "account_id": str(account.id),
                        "type": "usage_spike",
                        "today_usage": row.today_usage,
                        "avg_usage": row.avg_daily_usage,
                    }
                )

        return alerts_sent

    async def _should_send_alert(self, account_id: UUID, alert_type: str) -> bool:
        """Check if we should send an alert (avoid spam)"""
        # Don't send same alert more than once per day
        one_day_ago = datetime.utcnow() - timedelta(days=1)

        stmt = select(UsageEvent).where(
            UsageEvent.account_id == account_id,
            UsageEvent.event_type == f"alert_{alert_type}",
            UsageEvent.time > one_day_ago,
        )
        result = await self.db.execute(stmt)

        return result.first() is None

    async def _send_balance_alert(self, account: Account, alert_type: str):
        """Send low balance alert via email and webhook"""
        subject = (
            f"{'Critical' if alert_type == 'critical' else 'Low'} Credit Balance Alert"
        )

        message = f"""
        Your credit balance is running low.

        Current Balance: {account.credits_balance} credits

        {'⚠️ This is a critical alert. Please purchase credits immediately.' if alert_type == 'critical' else 'Consider purchasing more credits to avoid service interruption.'}

        Purchase credits at: https://app.foundrymatch.com/billing
        """

        # Send email
        await send_email(to=account.email, subject=subject, body=message)

        # Send webhook if configured
        if account.webhook_url:
            await send_webhook(
                url=account.webhook_url,
                data={
                    "type": f"credit.balance.{alert_type}",
                    "account_id": str(account.id),
                    "credits_remaining": account.credits_balance,
                    "timestamp": datetime.utcnow().isoformat(),
                },
            )

    async def _send_usage_spike_alert(
        self, account: Account, today_usage: int, avg_usage: float
    ):
        """Send usage spike alert"""
        subject = "Unusual Credit Usage Detected"

        message = f"""
        We've detected unusually high credit usage on your account today.

        Today's Usage: {today_usage} credits
        Average Daily Usage: {int(avg_usage)} credits
        Current Balance: {account.credits_balance} credits

        At this rate, your credits will last approximately {int(account.credits_balance / today_usage)} more days.

        View usage details at: https://app.foundrymatch.com/billing/usage
        """

        await send_email(to=account.email, subject=subject, body=message)

    async def _record_alert(self, account_id: UUID, alert_type: str, balance: int):
        """Record that an alert was sent"""
        event = UsageEvent(
            account_id=account_id,
            event_type=f"alert_{alert_type}",
            credits_consumed=0,
            metadata={
                "alert_type": alert_type,
                "balance_at_alert": balance,
                "timestamp": datetime.utcnow().isoformat(),
            },
        )
        self.db.add(event)
        await self.db.commit()
