from __future__ import annotations
import logging
from datetime import datetime, timedelta
from typing import List, Dict, Any, Optional
from uuid import UUID

from fastapi import APIRouter, Depends, Query, HTTPException, Response, Body
from sqlalchemy import select, and_, or_, func, case, String
from sqlalchemy.orm import selectinload
from sqlalchemy.ext.asyncio import AsyncSession
import pandas as pd

from ..models import Account, Job, Project
from ..models import UsageEvent
from ..auth import require_admin
from ..db import get_session

log = logging.getLogger(__name__)

router = APIRouter(prefix="/api/admin/customers", tags=["admin"])


# Customer Status Enum
class CustomerStatus:
    ACTIVE = "active"  # Has credits, recent usage
    AT_RISK = "at_risk"  # Low credits or declining usage
    CHURNED = "churned"  # No usage in 30+ days
    TRIAL = "trial"  # New account, < 7 days
    HIGH_VALUE = "high_value"  # Top 20% by revenue


@router.get("/overview")
async def get_customers_overview(
    db: AsyncSession = Depends(get_session),
    admin_account_id: str = Depends(require_admin),
) -> Dict[str, Any]:
    """High-level customer metrics for dashboard"""

    now = datetime.utcnow()
    thirty_days_ago = now - timedelta(days=30)
    seven_days_ago = now - timedelta(days=7)

    # Total accounts
    total_accounts = await db.scalar(select(func.count(Account.id)))

    # Active accounts (usage in last 30 days)
    active_accounts = await db.scalar(
        select(func.count(func.distinct(UsageEvent.account_id))).where(
            and_(UsageEvent.time >= thirty_days_ago, UsageEvent.credits_consumed > 0)
        )
    )

    # New accounts this week
    new_accounts = await db.scalar(
        select(func.count(Account.id)).where(Account.created_at >= seven_days_ago)
    )

    # Total revenue (all time)
    revenue_query = select(
        func.sum(UsageEvent.usage_metadata["amount"].as_float())
    ).where(UsageEvent.event_type.in_(["credit_purchase", "subscription_payment"]))
    total_revenue = await db.scalar(revenue_query) or 0

    # MRR estimate (last 30 days revenue)
    mrr_query = select(func.sum(UsageEvent.usage_metadata["amount"].as_float())).where(
        and_(
            UsageEvent.event_type.in_(["credit_purchase", "subscription_payment"]),
            UsageEvent.time >= thirty_days_ago,
        )
    )
    mrr_estimate = await db.scalar(mrr_query) or 0

    # At-risk accounts (< 100 credits)
    at_risk_accounts = await db.scalar(
        select(func.count(Account.id)).where(
            and_(Account.credits_balance < 100, Account.credits_balance > 0)
        )
    )

    return {
        "metrics": {
            "total_accounts": total_accounts,
            "active_accounts": active_accounts,
            "new_accounts_this_week": new_accounts,
            "at_risk_accounts": at_risk_accounts,
            "churn_rate": round(
                ((total_accounts - active_accounts) / total_accounts * 100)
                if total_accounts > 0
                else 0,
                1,
            ),
        },
        "revenue": {
            "total_all_time": round(total_revenue, 2),
            "mrr_estimate": round(mrr_estimate, 2),
            "arpu": round(
                mrr_estimate / active_accounts if active_accounts > 0 else 0, 2
            ),
        },
        "updated_at": now.isoformat(),
    }


@router.get("/list")
async def list_customers(
    status: Optional[str] = Query(None),
    search: Optional[str] = Query(None),
    sort_by: str = Query(
        "created_at", pattern="^(created_at|credits_balance|last_active|revenue)$"
    ),
    order: str = Query("desc", pattern="^(asc|desc)$"),
    limit: int = Query(50, ge=1, le=200),
    offset: int = Query(0, ge=0),
    db: AsyncSession = Depends(get_session),
    admin_account_id: str = Depends(require_admin),
) -> Dict[str, Any]:
    """List customers with filtering and sorting"""

    # Base query
    query = select(Account).options(selectinload(Account.users))

    # Search filter
    if search:
        search_term = f"%{search}%"
        query = query.where(
            or_(
                Account.name.ilike(search_term),
                Account.stripe_customer_id.ilike(search_term),
                Account.id.cast(String).ilike(search_term),
            )
        )

    # Get customers with enriched data
    customers = []
    result = await db.execute(query.limit(limit).offset(offset))
    accounts = result.scalars().all()

    for account in accounts:
        # Get customer metrics
        customer_data = await _get_customer_details(db, account)

        # Filter by status if specified
        if status and customer_data["status"] != status:
            continue

        customers.append(customer_data)

    # Sort results
    if sort_by == "revenue":
        customers.sort(key=lambda x: x["revenue"]["total"], reverse=(order == "desc"))
    elif sort_by == "last_active":
        customers.sort(
            key=lambda x: x["activity"]["last_active"] or "", reverse=(order == "desc")
        )
    elif sort_by == "credits_balance":
        customers.sort(key=lambda x: x["credits_balance"], reverse=(order == "desc"))
    else:  # created_at
        customers.sort(key=lambda x: x["created_at"], reverse=(order == "desc"))

    # Get total count
    count_query = select(func.count(Account.id))
    if search:
        count_query = count_query.where(
            or_(
                Account.name.ilike(search_term),
                Account.stripe_customer_id.ilike(search_term),
            )
        )
    total_count = await db.scalar(count_query)

    return {
        "customers": customers,
        "pagination": {
            "total": total_count,
            "limit": limit,
            "offset": offset,
            "has_more": offset + limit < total_count,
        },
    }


@router.get("/{account_id}")
async def get_customer_details(
    account_id: UUID,
    db: AsyncSession = Depends(get_session),
    admin_account_id: str = Depends(require_admin),
) -> Dict[str, Any]:
    """Get detailed information about a specific customer"""

    # Get account with related data
    stmt = (
        select(Account)
        .options(selectinload(Account.users), selectinload(Account.api_keys))
        .where(Account.id == account_id)
    )

    result = await db.execute(stmt)
    account = result.scalar_one_or_none()

    if not account:
        raise HTTPException(404, "Customer not found")

    # Get detailed metrics
    details = await _get_customer_details(db, account, include_timeline=True)

    return details


async def _get_customer_details(
    db: AsyncSession, account: Account, include_timeline: bool = False
) -> Dict[str, Any]:
    """Helper to get enriched customer data"""

    now = datetime.utcnow()
    thirty_days_ago = now - timedelta(days=30)

    # Get usage metrics
    usage_stats = await db.execute(
        select(
            func.sum(
                case(
                    (UsageEvent.credits_consumed > 0, UsageEvent.credits_consumed),
                    else_=0,
                )
            ).label("consumed"),
            func.sum(
                case(
                    (
                        UsageEvent.credits_consumed < 0,
                        func.abs(UsageEvent.credits_consumed),
                    ),
                    else_=0,
                )
            ).label("purchased"),
            func.max(UsageEvent.time).label("last_activity"),
        ).where(UsageEvent.account_id == account.id)
    )
    usage = usage_stats.first()

    # Get revenue
    revenue_stats = await db.execute(
        select(
            func.sum(UsageEvent.usage_metadata["amount"].as_float()).label("total"),
            func.count(UsageEvent.id).label("transaction_count"),
        ).where(
            and_(
                UsageEvent.account_id == account.id,
                UsageEvent.event_type.in_(["credit_purchase", "subscription_payment"]),
            )
        )
    )
    revenue = revenue_stats.first()

    # Get job statistics
    job_stats = await db.execute(
        select(
            func.count(Job.id).label("total_jobs"),
            func.count(case((Job.status == "completed", 1))).label("successful_jobs"),
            func.count(case((Job.status == "failed", 1))).label("failed_jobs"),
        ).where(
            Job.project_id.in_(
                select(Project.id).where(Project.account_id == account.id)
            )
        )
    )
    jobs = job_stats.first()

    # Determine status
    status = _determine_customer_status(
        account, usage.last_activity if usage else None, revenue.total if revenue else 0
    )

    # Build response
    customer_data = {
        "id": str(account.id),
        "name": account.name,
        "status": status,
        "created_at": account.created_at.isoformat(),
        "credits_balance": account.credits_balance,
        "stripe_customer_id": account.stripe_customer_id,
        "subscription_id": account.stripe_subscription_id,
        "users": [
            {
                "id": str(user.id),
                "email": user.email,
                "role": user.role,
                "last_login": user.last_login.isoformat() if user.last_login else None,
            }
            for user in account.users
        ],
        "activity": {
            "last_active": usage.last_activity.isoformat()
            if usage and usage.last_activity
            else None,
            "total_credits_consumed": usage.consumed or 0 if usage else 0,
            "total_credits_purchased": usage.purchased or 0 if usage else 0,
            "days_since_last_activity": (now - usage.last_activity).days
            if usage and usage.last_activity
            else None,
        },
        "revenue": {
            "total": round(revenue.total or 0, 2) if revenue else 0,
            "transaction_count": revenue.transaction_count or 0 if revenue else 0,
            "ltv": round(revenue.total or 0, 2) if revenue else 0,  # Lifetime value
        },
        "usage": {
            "total_jobs": jobs.total_jobs if jobs else 0,
            "successful_jobs": jobs.successful_jobs if jobs else 0,
            "failed_jobs": jobs.failed_jobs if jobs else 0,
            "success_rate": round(jobs.successful_jobs / jobs.total_jobs * 100, 1)
            if jobs and jobs.total_jobs > 0
            else 0,
        },
        "api_keys": [
            {
                "id": str(key.id),
                "name": key.name,
                "prefix": key.key_prefix,
                "last_used": key.last_used_at.isoformat() if key.last_used_at else None,
                "is_active": key.is_active,
            }
            for key in account.api_keys
        ]
        if hasattr(account, "api_keys")
        else [],
    }

    # Add timeline if requested
    if include_timeline:
        customer_data["timeline"] = await _get_customer_timeline(db, account.id)

    return customer_data


def _determine_customer_status(
    account: Account, last_activity: Optional[datetime], total_revenue: float
) -> str:
    """Determine customer status based on activity and metrics"""

    now = datetime.utcnow()

    # New trial account
    if (now - account.created_at).days <= 7:
        return CustomerStatus.TRIAL

    # Churned - no activity in 30+ days
    if not last_activity or (now - last_activity).days > 30:
        return CustomerStatus.CHURNED

    # High value - top revenue or high usage
    if total_revenue > 500:  # Adjust threshold as needed
        return CustomerStatus.HIGH_VALUE

    # At risk - low credits
    if account.credits_balance < 100:
        return CustomerStatus.AT_RISK

    # Default to active
    return CustomerStatus.ACTIVE


async def _get_customer_timeline(
    db: AsyncSession, account_id: UUID, limit: int = 20
) -> List[Dict[str, Any]]:
    """Get activity timeline for customer"""

    # Get recent events
    stmt = (
        select(UsageEvent)
        .where(UsageEvent.account_id == account_id)
        .order_by(UsageEvent.time.desc())
        .limit(limit)
    )

    result = await db.execute(stmt)
    events = result.scalars().all()

    timeline = []
    for event in events:
        timeline_item = {
            "time": event.time.isoformat(),
            "type": event.event_type,
            "credits": event.credits_consumed,
        }

        # Add relevant metadata
        if event.event_type == "credit_purchase":
            timeline_item["amount"] = event.usage_metadata.get("amount", 0)
            timeline_item["description"] = (
                f"Purchased {abs(event.credits_consumed)} credits"
            )
        elif event.event_type == "match_job":
            timeline_item["description"] = (
                f"Ran match job ({event.credits_consumed} credits)"
            )
            timeline_item["job_id"] = event.usage_metadata.get("job_id")

        timeline.append(timeline_item)

    return timeline


@router.get("/export/csv")
async def export_customers_csv(
    db: AsyncSession = Depends(get_session),
    admin_account_id: str = Depends(require_admin),
):
    """Export customer data as CSV"""

    # Get all customers
    stmt = select(Account).options(selectinload(Account.users))
    result = await db.execute(stmt)
    accounts = result.scalars().all()

    # Build CSV data
    rows = []
    for account in accounts:
        customer = await _get_customer_details(db, account)

        rows.append(
            {
                "Account ID": customer["id"],
                "Account Name": customer["name"],
                "Status": customer["status"],
                "Created Date": customer["created_at"],
                "Credits Balance": customer["credits_balance"],
                "Total Revenue": customer["revenue"]["total"],
                "Total Jobs": customer["usage"]["total_jobs"],
                "Last Active": customer["activity"]["last_active"],
                "Primary Email": customer["users"][0]["email"]
                if customer["users"]
                else "",
                "User Count": len(customer["users"]),
            }
        )

    # Convert to CSV
    df = pd.DataFrame(rows)
    csv_data = df.to_csv(index=False)

    return Response(
        content=csv_data,
        media_type="text/csv",
        headers={
            "Content-Disposition": f"attachment; filename=customers_{datetime.now():%Y%m%d}.csv"
        },
    )


@router.post("/{account_id}/add-credits")
async def add_credits_to_customer(
    account_id: UUID,
    credits: int = Body(..., gt=0),
    reason: str = Body(...),
    db: AsyncSession = Depends(get_session),
    admin_account_id: str = Depends(require_admin),
):
    """Manually add credits to a customer account"""

    account = await db.get(Account, account_id)
    if not account:
        raise HTTPException(404, "Customer not found")

    # Add credits
    account.credits_balance += credits

    # Track the credit addition
    event = UsageEvent(
        account_id=account_id,
        event_type="admin_credit_adjustment",
        credits_consumed=-credits,  # Negative = credits added
        usage_metadata={
            "reason": reason,
            "admin_id": str(admin_account_id),
            "previous_balance": account.credits_balance - credits,
        },
    )

    db.add(account)
    db.add(event)
    await db.commit()

    return {
        "success": True,
        "new_balance": account.credits_balance,
        "credits_added": credits,
    }


@router.get("/health-scores")
async def get_customer_health_scores(
    db: AsyncSession = Depends(get_session),
    admin_account_id: str = Depends(require_admin),
) -> Dict[str, Any]:
    """Get customer health metrics for success monitoring"""

    now = datetime.utcnow()
    thirty_days_ago = now - timedelta(days=30)
    sixty_days_ago = now - timedelta(days=60)

    # Get usage trends
    current_period = await db.execute(
        select(
            UsageEvent.account_id,
            func.sum(UsageEvent.credits_consumed).label("credits"),
        )
        .where(
            and_(UsageEvent.time >= thirty_days_ago, UsageEvent.credits_consumed > 0)
        )
        .group_by(UsageEvent.account_id)
    )

    previous_period = await db.execute(
        select(
            UsageEvent.account_id,
            func.sum(UsageEvent.credits_consumed).label("credits"),
        )
        .where(
            and_(
                UsageEvent.time >= sixty_days_ago,
                UsageEvent.time < thirty_days_ago,
                UsageEvent.credits_consumed > 0,
            )
        )
        .group_by(UsageEvent.account_id)
    )

    # Calculate health scores
    current_usage = {row.account_id: row.credits for row in current_period}
    previous_usage = {row.account_id: row.credits for row in previous_period}

    health_scores = []

    for account_id, current_credits in current_usage.items():
        previous_credits = previous_usage.get(account_id, 0)

        # Calculate growth rate
        if previous_credits > 0:
            growth_rate = (
                (current_credits - previous_credits) / previous_credits
            ) * 100
        else:
            growth_rate = 100 if current_credits > 0 else 0

        # Get account details
        account = await db.get(Account, account_id)
        if not account:
            continue

        # Calculate health score (0-100)
        health_score = calculate_health_score(
            credits_balance=account.credits_balance,
            usage_growth=growth_rate,
            days_active=(now - account.created_at).days,
        )

        health_scores.append(
            {
                "account_id": str(account_id),
                "account_name": account.name,
                "health_score": health_score,
                "usage_trend": "growing"
                if growth_rate > 10
                else "declining"
                if growth_rate < -10
                else "stable",
                "growth_rate": round(growth_rate, 1),
                "current_period_usage": current_credits,
                "previous_period_usage": previous_credits,
            }
        )

    # Sort by health score
    health_scores.sort(key=lambda x: x["health_score"])

    return {
        "at_risk_customers": health_scores[:10],  # Bottom 10
        "healthy_customers": health_scores[-10:],  # Top 10
        "summary": {
            "total_monitored": len(health_scores),
            "at_risk_count": len([h for h in health_scores if h["health_score"] < 50]),
            "healthy_count": len([h for h in health_scores if h["health_score"] >= 70]),
        },
    }


def calculate_health_score(
    credits_balance: int, usage_growth: float, days_active: int
) -> int:
    """Calculate customer health score (0-100)"""

    score = 50  # Base score

    # Credits balance factor (0-30 points)
    if credits_balance >= 1000:
        score += 30
    elif credits_balance >= 500:
        score += 20
    elif credits_balance >= 100:
        score += 10
    elif credits_balance < 50:
        score -= 20

    # Usage growth factor (0-40 points)
    if usage_growth > 50:
        score += 40
    elif usage_growth > 20:
        score += 30
    elif usage_growth > 0:
        score += 20
    elif usage_growth > -20:
        score += 10
    else:
        score -= 10

    # Tenure factor (0-30 points)
    if days_active > 180:
        score += 30
    elif days_active > 90:
        score += 20
    elif days_active > 30:
        score += 10

    return max(0, min(100, score))
