import json
from datetime import datetime
from typing import Annotated, Any, Optional

from fastapi import APIRouter, Depends
from pydantic import BaseModel, Field
from sqlalchemy.orm import Session

from app.api.customers import _customer_to_response, _normalize_customer_data, _sync_contacts
from app.core.database import get_db
from app.core.models import Customer, Invoice, InvoiceEvent, InvoiceItem, InvoiceStatus
from app.core.security import get_current_user
from app.services.ai_service import (
    chat_with_ai,
    parse_ai_draft,
    validate_customer_data,
    validate_draft_data,
)
from app.services.ai_query_executor import execute_ai_query
from app.services.invoice_service import calculate_item, generate_invoice_number

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


class ChatRequest(BaseModel):
    message: str
    thread_id: Optional[str] = None
    context: list[dict] = Field(default_factory=list)


class ChatResponse(BaseModel):
    reply: str
    action: Optional[str] = None
    draft_id: Optional[int] = None
    customer_id: Optional[int] = None
    source: Optional[str] = None
    tool_name: Optional[str] = None
    result: Optional[dict[str, Any]] = None


def _prepare_customer_payload(customer_data: dict) -> dict:
    prepared = dict(customer_data or {})
    contacts = prepared.get("contacts", [])
    if contacts is None or not isinstance(contacts, list):
        contacts = []

    fallback_name = str(prepared.get("contact_name") or "").strip()
    fallback_email = str(prepared.get("email") or "").strip()
    fallback_phone = str(prepared.get("phone") or "").strip()
    fallback_mobile = str(prepared.get("mobile") or "").strip()

    normalized_contacts = []
    for contact in contacts:
        if not isinstance(contact, dict):
            continue

        entry = dict(contact)
        name = str(entry.get("name") or "").strip()
        email = str(entry.get("email") or "").strip()
        phone = str(entry.get("phone") or "").strip()
        mobile = str(entry.get("mobile") or "").strip()
        title = str(entry.get("title") or "").strip()
        notes = str(entry.get("notes") or "").strip()

        if not any([name, email, phone, mobile, title, notes]):
            continue

        if not name:
            entry["name"] = fallback_name or str(prepared.get("company_name") or prepared.get("name") or "").strip()
        if not entry.get("email") and fallback_email:
            entry["email"] = fallback_email
        if not entry.get("phone") and fallback_phone:
            entry["phone"] = fallback_phone
        if not entry.get("mobile") and fallback_mobile:
            entry["mobile"] = fallback_mobile
        normalized_contacts.append(entry)

    if not normalized_contacts and (fallback_name or fallback_email or fallback_phone or fallback_mobile):
        normalized_contacts.append(
            {
                "name": fallback_name or str(prepared.get("company_name") or prepared.get("name") or "").strip(),
                "email": fallback_email or None,
                "phone": fallback_phone or None,
                "mobile": fallback_mobile or None,
                "is_primary": True,
            }
        )

    prepared["contacts"] = normalized_contacts
    return prepared


def _create_invoice_draft(db: Session, draft_data: dict) -> tuple[str, str, int | None]:
    valid, error = validate_draft_data(draft_data)
    if not valid:
        return f"AI draft validation error: {error}", "error", None

    customer_name = draft_data["customer_name"]
    customer = db.query(Customer).filter(Customer.name.ilike(f"%{customer_name}%")).first()
    if not customer:
        return f"Customer '{customer_name}' not found. Please check the name.", "query", None

    invoice = Invoice(
        invoice_number=generate_invoice_number(db),
        customer_id=customer.id,
        invoice_date=draft_data.get("invoice_date", datetime.now().date()),
        due_date=draft_data.get("due_date"),
        currency=draft_data.get("currency", "NZD"),
        notes=draft_data.get("notes", ""),
        status=InvoiceStatus.draft,
    )
    db.add(invoice)
    db.flush()

    for item_data in draft_data["items"]:
        qty = float(item_data.get("quantity", 1))
        price = float(item_data.get("unit_price", 0))
        rate = float(item_data.get("tax_rate", 0.15))
        calculated = calculate_item({"quantity": qty, "unit_price": price, "tax_rate": rate})

        item = InvoiceItem(
            invoice_id=invoice.id,
            description=item_data.get("description", "Item"),
            quantity=qty,
            unit_price=price,
            tax_rate=rate,
            subtotal=calculated["subtotal"],
            tax_amount=calculated["tax_amount"],
            total=calculated["total"],
        )
        db.add(item)

    items = db.query(InvoiceItem).filter(InvoiceItem.invoice_id == invoice.id).all()
    invoice.subtotal = sum(i.subtotal for i in items)
    invoice.total_tax = sum(i.tax_amount for i in items)
    invoice.total_amount = sum(i.total for i in items)

    db.commit()

    event = InvoiceEvent(
        invoice_id=invoice.id,
        event_type="ai_drafted",
        actor_type="ai",
        actor_name="AI Assistant",
        message="AI draft created from chat",
        metadata_json=json.dumps({"raw": draft_data}),
    )
    db.add(event)
    db.commit()

    reply = (
        f"Invoice draft created: {invoice.invoice_number}\n\n"
        "Review it in the Invoices page and click 'Issue' when ready."
    )
    return reply, "create_draft", invoice.id


def _create_customer(db: Session, customer_data: dict) -> tuple[str, str, int | None]:
    prepared_data = _prepare_customer_payload(customer_data)
    valid, error = validate_customer_data(prepared_data)
    if not valid:
        return f"AI customer validation error: {error}", "error", None

    customer_name = str(prepared_data.get("name") or prepared_data.get("company_name") or "").strip()
    existing = (
        db.query(Customer)
        .filter((Customer.name.ilike(customer_name)) | (Customer.company_name.ilike(customer_name)))
        .first()
    )
    if existing:
        return f"Customer '{customer_name}' already exists.", "query", None

    payload = {
        "name": prepared_data.get("name"),
        "company_name": prepared_data.get("company_name"),
        "contact_name": prepared_data.get("contact_name"),
        "email": prepared_data.get("email"),
        "phone": prepared_data.get("phone"),
        "mobile": prepared_data.get("mobile"),
        "address": prepared_data.get("address"),
        "website": prepared_data.get("website"),
        "customer_type": prepared_data.get("customer_type", "project"),
        "status": prepared_data.get("status", "active"),
        "notes": prepared_data.get("notes"),
        "contacts": prepared_data.get("contacts", []),
    }

    normalized_data, contacts = _normalize_customer_data(payload)
    customer = Customer(**normalized_data)
    if contacts:
        _sync_contacts(db, customer, contacts)

    db.add(customer)
    db.commit()
    db.refresh(customer)

    customer_response = _customer_to_response(db, customer)
    reply = (
        f"Customer created: {customer_response.name}\n\n"
        f"Company: {customer_response.company_name or '-'}\n"
        f"Primary contact: {customer_response.contact_name or '-'}\n"
        f"Email: {customer_response.email or '-'}\n"
        f"Phone: {customer_response.phone or '-'}\n"
        f"Mobile: {customer_response.mobile or '-'}"
    )
    return reply, "create_customer", customer.id


@router.post("/chat", response_model=ChatResponse)
async def chat(
    req: ChatRequest,
    current_user: Annotated[dict, Depends(get_current_user)],
    db: Session = Depends(get_db),
):
    """Chat with the AI assistant."""
    response = await chat_with_ai(req.message, context=req.context)
    reply = response.get("content", "Sorry, AI is currently unavailable.")

    draft_data = parse_ai_draft(reply)
    action = None
    draft_id = None
    customer_id = None
    source = None
    tool_name = None
    result = None

    if draft_data and draft_data.get("action") == "query":
        query_result = execute_ai_query(db, draft_data)
        reply = query_result.get("reply", reply)
        action = query_result.get("action")
        source = query_result.get("source")
        tool_name = query_result.get("tool_name")
        result = query_result.get("result")
    elif draft_data and draft_data.get("action") == "create_invoice_draft":
        reply, action, draft_id = _create_invoice_draft(db, draft_data)
    elif draft_data and draft_data.get("action") == "create_customer":
        prepared_data = _prepare_customer_payload(draft_data)
        valid, error = validate_customer_data(prepared_data)
        if not valid:
            reply = f"AI customer validation error: {error}"
            action = "error"
        elif not prepared_data.get("confirmed"):
            action = "confirm_create_customer"
        else:
            reply, action, customer_id = _create_customer(db, prepared_data)

    return ChatResponse(
        reply=reply,
        action=action,
        draft_id=draft_id,
        customer_id=customer_id,
        source=source,
        tool_name=tool_name,
        result=result,
    )
