from datetime import datetime
from typing import Annotated

from fastapi import APIRouter, Depends, HTTPException, Query, status
from pydantic import BaseModel, ConfigDict
from sqlalchemy import func
from sqlalchemy.orm import Session

from app.core.database import get_db
from app.core.models import Customer, CustomerStatus, Invoice, Subscription
from app.api.auth import get_current_user

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


# ── Pydantic models ───────────────────────────────────────────────────────────

class CustomerBase(BaseModel):
    name: str
    contact_name: str | None = None
    email: str | None = None
    phone: str | None = None
    address: str | None = None
    customer_type: str = "project"
    status: str = "active"
    notes: str | None = None


class CustomerCreate(CustomerBase):
    pass


class CustomerUpdate(BaseModel):
    name: str | None = None
    contact_name: str | None = None
    email: str | None = None
    phone: str | None = None
    address: str | None = None
    customer_type: str | None = None
    status: str | None = None
    notes: str | None = None


class CustomerResponse(CustomerBase):
    id: int
    created_at: datetime
    updated_at: datetime | None
    invoices_count: int = 0
    subscriptions_count: int = 0

    model_config = ConfigDict(arbitrary_allowed_types=True, from_attributes=True)


class PaginatedCustomers(BaseModel):
    items: list[CustomerResponse]
    total: int
    page: int
    page_size: int


# ── Helpers ───────────────────────────────────────────────────────────────────

def _build_counts(db: Session, customer_id: int) -> tuple[int, int]:
    """Return (invoices_count, subscriptions_count) for a customer."""
    invoices_count = db.query(func.count(Invoice.id)).filter(
        Invoice.customer_id == customer_id
    ).scalar() or 0
    subscriptions_count = db.query(func.count(Subscription.id)).filter(
        Subscription.customer_id == customer_id
    ).scalar() or 0
    return invoices_count, subscriptions_count


def _customer_to_response(db: Session, customer: Customer) -> CustomerResponse:
    invoices_count, subscriptions_count = _build_counts(db, customer.id)
    return CustomerResponse(
        id=customer.id,
        name=customer.name,
        contact_name=customer.contact_name,
        email=customer.email,
        phone=customer.phone,
        address=customer.address,
        customer_type=customer.customer_type.value if hasattr(customer.customer_type, 'value') else customer.customer_type,
        status=customer.status.value if hasattr(customer.status, 'value') else customer.status,
        notes=customer.notes,
        created_at=customer.created_at,
        updated_at=customer.updated_at,
        invoices_count=invoices_count,
        subscriptions_count=subscriptions_count,
    )


# ── Endpoints ─────────────────────────────────────────────────────────────────

@router.get("", response_model=PaginatedCustomers)
def list_customers(
    current_user: Annotated[dict, Depends(get_current_user)],
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=100),
    search: str = Query("", max_length=200),
    status_filter: str = Query("", alias="status"),
    db: Session = Depends(get_db),
):
    """List all customers with pagination and optional search/status filter."""
    query = db.query(Customer)

    if search:
        term = f"%{search}%"
        query = query.filter(
            (Customer.name.ilike(term)) | (Customer.email.ilike(term))
        )

    if status_filter:
        query = query.filter(Customer.status == status_filter)
    else:
        # Default: exclude paused customers
        query = query.filter(Customer.status != CustomerStatus.paused)

    total = query.count()
    items = (
        query.order_by(Customer.id.desc())
        .offset((page - 1) * page_size)
        .limit(page_size)
        .all()
    )

    return PaginatedCustomers(
        items=[_customer_to_response(db, c) for c in items],
        total=total,
        page=page,
        page_size=page_size,
    )


@router.post("", response_model=CustomerResponse, status_code=status.HTTP_201_CREATED)
def create_customer(
    data: CustomerCreate,
    current_user: Annotated[dict, Depends(get_current_user)],
    db: Session = Depends(get_db),
):
    """Create a new customer."""
    customer = Customer(**data.model_dump())
    db.add(customer)
    db.commit()
    db.refresh(customer)
    return _customer_to_response(db, customer)


@router.get("/{customer_id}", response_model=CustomerResponse)
def get_customer(
    customer_id: int,
    current_user: Annotated[dict, Depends(get_current_user)],
    db: Session = Depends(get_db),
):
    """Get a single customer by ID with invoice/subscription counts."""
    customer = db.get(Customer, customer_id)
    if not customer:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Customer not found")
    return _customer_to_response(db, customer)


@router.put("/{customer_id}", response_model=CustomerResponse)
def update_customer(
    customer_id: int,
    data: CustomerUpdate,
    current_user: Annotated[dict, Depends(get_current_user)],
    db: Session = Depends(get_db),
):
    """Update an existing customer."""
    customer = db.get(Customer, customer_id)
    if not customer:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Customer not found")

    update_data = data.model_dump(exclude_unset=True)
    for field, value in update_data.items():
        setattr(customer, field, value)

    db.commit()
    db.refresh(customer)
    return _customer_to_response(db, customer)


@router.delete("/{customer_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_customer(
    customer_id: int,
    current_user: Annotated[dict, Depends(get_current_user)],
    db: Session = Depends(get_db),
):
    """Soft delete a customer (set status to paused)."""
    customer = db.get(Customer, customer_id)
    if not customer:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Customer not found")

    customer.status = CustomerStatus.paused
    db.commit()