from datetime import datetime
from typing import Annotated

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

from app.core.database import get_db
from app.core.models import Customer, CustomerContact, CustomerStatus, Invoice, Subscription
from app.core.security import get_current_user

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


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

class CustomerContactInput(BaseModel):
    name: str
    title: str | None = None
    email: str | None = None
    phone: str | None = None
    mobile: str | None = None
    is_primary: bool = False
    notes: str | None = None


class CustomerContactResponse(CustomerContactInput):
    id: int
    customer_id: int
    created_at: datetime | None = None
    updated_at: datetime | None = None

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


class CustomerBase(BaseModel):
    name: str
    company_name: str | None = None
    contact_name: str | None = None
    email: str | None = None
    phone: str | None = None
    mobile: str | None = None
    address: str | None = None
    website: str | None = None
    customer_type: str = "project"
    status: str = "active"
    notes: str | None = None
    contacts: list[CustomerContactInput] = Field(default_factory=list)


class CustomerCreate(CustomerBase):
    pass


class CustomerUpdate(BaseModel):
    name: str | None = None
    company_name: str | None = None
    contact_name: str | None = None
    email: str | None = None
    phone: str | None = None
    mobile: str | None = None
    address: str | None = None
    website: str | None = None
    customer_type: str | None = None
    status: str | None = None
    notes: str | None = None
    contacts: list[CustomerContactInput] | None = None


class CustomerResponse(CustomerBase):
    id: int
    created_at: datetime
    updated_at: datetime | None
    invoices_count: int = 0
    subscriptions_count: int = 0
    contacts: list[CustomerContactResponse] = Field(default_factory=list)

    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)
    contacts = sorted(customer.contacts, key=lambda c: (not c.is_primary, c.id))
    primary_contact = next((c for c in contacts if c.is_primary), contacts[0] if contacts else None)
    return CustomerResponse(
        id=customer.id,
        name=customer.name,
        company_name=customer.company_name,
        contact_name=customer.contact_name or (primary_contact.name if primary_contact else None),
        email=customer.email or (primary_contact.email if primary_contact else None),
        phone=customer.phone or (primary_contact.phone if primary_contact else None),
        mobile=customer.mobile or (primary_contact.mobile if primary_contact else None),
        address=customer.address,
        website=customer.website,
        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,
        contacts=[CustomerContactResponse.model_validate(c) for c in contacts],
    )


def _normalize_customer_data(data: dict) -> tuple[dict, list[CustomerContactInput] | None]:
    contacts = data.pop("contacts", None)
    if data.get("company_name") and not data.get("name"):
        data["name"] = data["company_name"]
    if data.get("name") and not data.get("company_name"):
        data["company_name"] = data["name"]
    return data, contacts


def _sync_contacts(db: Session, customer: Customer, contacts: list[CustomerContactInput | dict]) -> None:
    customer.contacts.clear()
    if not contacts:
        return

    normalized = [
        contact if isinstance(contact, CustomerContactInput) else CustomerContactInput(**contact)
        for contact in contacts
    ]
    primary_seen = any(contact.is_primary for contact in normalized)
    for index, contact in enumerate(normalized):
        contact_data = contact.model_dump()
        if not primary_seen and index == 0:
            contact_data["is_primary"] = True
        customer.contacts.append(CustomerContact(**contact_data))


# ── 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.company_name.ilike(term))
            | (Customer.email.ilike(term))
            | (Customer.website.ilike(term))
            | (Customer.contacts.any(CustomerContact.name.ilike(term)))
            | (Customer.contacts.any(CustomerContact.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_data, contacts = _normalize_customer_data(data.model_dump())
    customer = Customer(**customer_data)
    if contacts:
        _sync_contacts(db, customer, contacts)
    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, contacts = _normalize_customer_data(data.model_dump(exclude_unset=True))
    for field, value in update_data.items():
        setattr(customer, field, value)
    if contacts is not None:
        _sync_contacts(db, customer, contacts)

    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()
