import asyncio
import logging
from fastapi import APIRouter, Depends, Query, BackgroundTasks, Body
from typing import Optional
from sqlalchemy.ext.asyncio import AsyncSession

from auth.models import TokenPayload
from auth.dependencies import require_permission
from crm.models import CustomerCreate, CustomerUpdate, CustomerInDB, CustomerListResponse, TransactionEntry
from crm import service, nextcloud
from config import settings
from database.postgres import get_pg_session
from shared.audit import log_action

router = APIRouter(prefix="/api/crm/customers", tags=["crm-customers"])
logger = logging.getLogger(__name__)

# ── Diff helpers ──────────────────────────────────────────────────────────────

_SCALAR_FIELDS = {
    "name", "surname", "title", "organization", "religion", "language",
    "relationship_status", "nextcloud_folder",
}
_SKIP_FIELDS = {"updated_at", "firestore_id", "id"}


def _scalar_diff(old, new) -> dict:
    result = {}
    for f in _SCALAR_FIELDS:
        ov = getattr(old, f, None)
        nv = getattr(new, f, None)
        if ov != nv:
            result[f] = {"old": ov, "new": nv}
    return result


def _list_diff(field: str, old_list: list, new_list: list, label_fn) -> dict:
    old_labels = {label_fn(i) for i in (old_list or [])}
    new_labels = {label_fn(i) for i in (new_list or [])}
    added   = new_labels - old_labels
    removed = old_labels - new_labels
    result = {}
    if added:
        result[f"{field}.added"] = {"old": None, "new": sorted(added)}
    if removed:
        result[f"{field}.removed"] = {"old": sorted(removed), "new": None}
    return result


def _customer_diff(old, new, changed_fields: set) -> dict:
    changes = _scalar_diff(old, new)

    # contacts — keyed by type+value string
    if "contacts" in changed_fields:
        changes.update(_list_diff(
            "contacts",
            old.contacts or [],
            new.contacts or [],
            lambda c: f"{c.get('type','?')}:{c.get('value','?')}" if isinstance(c, dict)
                      else f"{getattr(c,'type','?')}:{getattr(c,'value','?')}",
        ))

    # location — flatten to individual sub-fields
    if "location" in changed_fields:
        old_loc = old.location or {}
        new_loc = new.location or {}
        if isinstance(old_loc, object) and not isinstance(old_loc, dict):
            old_loc = old_loc.model_dump() if hasattr(old_loc, "model_dump") else {}
        if isinstance(new_loc, object) and not isinstance(new_loc, dict):
            new_loc = new_loc.model_dump() if hasattr(new_loc, "model_dump") else {}
        for k in set(old_loc) | set(new_loc):
            ov, nv = old_loc.get(k), new_loc.get(k)
            if ov != nv:
                changes[f"location.{k}"] = {"old": ov, "new": nv}

    # tags
    if "tags" in changed_fields:
        old_tags = set(old.tags or [])
        new_tags = set(new.tags or [])
        if old_tags != new_tags:
            changes["tags"] = {"old": sorted(old_tags), "new": sorted(new_tags)}

    return changes


@router.get("", response_model=CustomerListResponse)
async def list_customers(
    search: Optional[str] = Query(None),
    tag: Optional[str] = Query(None),
    sort: Optional[str] = Query(None),
    _user: TokenPayload = Depends(require_permission("crm", "view")),
):
    customers = service.list_customers(search=search, tag=tag, sort=sort)
    if sort == "latest_comm":
        customers = await service.list_customers_sorted_by_latest_comm(customers)
    return CustomerListResponse(customers=customers, total=len(customers))


@router.get("/tags", response_model=list[str])
def list_tags(
    _user: TokenPayload = Depends(require_permission("crm", "view")),
):
    return service.list_all_tags()


@router.get("/{customer_id}", response_model=CustomerInDB)
def get_customer(
    customer_id: str,
    _user: TokenPayload = Depends(require_permission("crm", "view")),
):
    return service.get_customer(customer_id)


@router.post("", response_model=CustomerInDB, status_code=201)
async def create_customer(
    body: CustomerCreate,
    background_tasks: BackgroundTasks,
    _user: TokenPayload = Depends(require_permission("crm", "edit")),
    db: AsyncSession = Depends(get_pg_session),
):
    customer = service.create_customer(body)
    if settings.nextcloud_url:
        background_tasks.add_task(_init_nextcloud_folder, customer)
    label = " ".join(filter(None, [customer.name, customer.surname])) or customer.organization or customer.id
    await log_action(db, _user.sub, _user.name or _user.email, "CREATE", "customer",
                     customer.id, label)
    return customer


async def _init_nextcloud_folder(customer) -> None:
    try:
        nc_path = service.get_customer_nc_path(customer)
        base = f"customers/{nc_path}"
        for sub in ("media", "documents", "sent", "received"):
            await nextcloud.ensure_folder(f"{base}/{sub}")
        await nextcloud.write_info_file(base, customer.name, customer.id)
    except Exception as e:
        logger.warning("Nextcloud folder init failed for customer %s: %s", customer.id, e)


@router.put("/{customer_id}", response_model=CustomerInDB)
async def update_customer(
    customer_id: str,
    body: CustomerUpdate,
    _user: TokenPayload = Depends(require_permission("crm", "edit")),
    db: AsyncSession = Depends(get_pg_session),
):
    old = service.get_customer(customer_id)
    customer = service.update_customer(customer_id, body)
    label = " ".join(filter(None, [customer.name, customer.surname])) or customer.organization or customer_id
    changes = _customer_diff(old, customer, body.model_fields_set)
    await log_action(db, _user.sub, _user.name or _user.email, "UPDATE", "customer",
                     customer_id, label, changes=changes or None)
    return customer


@router.delete("/{customer_id}", status_code=204)
async def delete_customer(
    customer_id: str,
    wipe_comms: bool = Query(False),
    wipe_files: bool = Query(False),
    wipe_nextcloud: bool = Query(False),
    _user: TokenPayload = Depends(require_permission("crm", "edit")),
    db: AsyncSession = Depends(get_pg_session),
):
    customer = service.delete_customer(customer_id)
    nc_path = service.get_customer_nc_path(customer)

    if wipe_comms or wipe_nextcloud:
        await service.delete_customer_comms(customer_id)

    if wipe_files or wipe_nextcloud:
        await service.delete_customer_media_entries(customer_id)

    if settings.nextcloud_url:
        folder = f"customers/{nc_path}"
        if wipe_nextcloud:
            try:
                await nextcloud.delete_file(folder)
            except Exception as e:
                logger.warning("Could not delete NC folder for customer %s: %s", customer_id, e)
        elif wipe_files:
            stale_folder = f"customers/STALE_{nc_path}"
            try:
                await nextcloud.rename_folder(folder, stale_folder)
            except Exception as e:
                logger.warning("Could not rename NC folder for customer %s: %s", customer_id, e)

    label = " ".join(filter(None, [customer.name, customer.surname])) or customer.organization or customer_id
    await log_action(db, _user.sub, _user.name or _user.email, "DELETE", "customer",
                     customer_id, label)


@router.get("/{customer_id}/last-comm-direction")
async def get_last_comm_direction(
    customer_id: str,
    _user: TokenPayload = Depends(require_permission("crm", "view")),
):
    result = await service.get_last_comm_direction(customer_id)
    return result


# ── Relationship Status ───────────────────────────────────────────────────────

@router.patch("/{customer_id}/relationship-status", response_model=CustomerInDB)
async def update_relationship_status(
    customer_id: str,
    body: dict = Body(...),
    _user: TokenPayload = Depends(require_permission("crm", "edit")),
    db: AsyncSession = Depends(get_pg_session),
):
    customer = service.update_relationship_status(customer_id, body.get("status", ""))
    label = " ".join(filter(None, [customer.name, customer.surname])) or customer.organization or customer_id
    await log_action(db, _user.sub, _user.name or _user.email, "STATUS_CHANGE", "customer",
                     customer_id, label, meta={"status": body.get("status", "")})
    return customer


# ── Technical Issues ──────────────────────────────────────────────────────────

@router.post("/{customer_id}/technical-issues", response_model=CustomerInDB)
def add_technical_issue(
    customer_id: str,
    body: dict = Body(...),
    _user: TokenPayload = Depends(require_permission("crm", "edit")),
):
    return service.add_technical_issue(
        customer_id,
        note=body.get("note", ""),
        opened_by=body.get("opened_by", ""),
        date=body.get("date"),
    )


@router.patch("/{customer_id}/technical-issues/{index}/resolve", response_model=CustomerInDB)
def resolve_technical_issue(
    customer_id: str,
    index: int,
    body: dict = Body(...),
    _user: TokenPayload = Depends(require_permission("crm", "edit")),
):
    return service.resolve_technical_issue(customer_id, index, body.get("resolved_by", ""))


@router.patch("/{customer_id}/technical-issues/{index}", response_model=CustomerInDB)
def edit_technical_issue(
    customer_id: str,
    index: int,
    body: dict = Body(...),
    _user: TokenPayload = Depends(require_permission("crm", "edit")),
):
    return service.edit_technical_issue(customer_id, index, body.get("note", ""), body.get("opened_date"))


@router.delete("/{customer_id}/technical-issues/{index}", response_model=CustomerInDB)
def delete_technical_issue(
    customer_id: str,
    index: int,
    _user: TokenPayload = Depends(require_permission("crm", "edit")),
):
    return service.delete_technical_issue(customer_id, index)


# ── Install Support ───────────────────────────────────────────────────────────

@router.post("/{customer_id}/install-support", response_model=CustomerInDB)
def add_install_support(
    customer_id: str,
    body: dict = Body(...),
    _user: TokenPayload = Depends(require_permission("crm", "edit")),
):
    return service.add_install_support(
        customer_id,
        note=body.get("note", ""),
        opened_by=body.get("opened_by", ""),
        date=body.get("date"),
    )


@router.patch("/{customer_id}/install-support/{index}/resolve", response_model=CustomerInDB)
def resolve_install_support(
    customer_id: str,
    index: int,
    body: dict = Body(...),
    _user: TokenPayload = Depends(require_permission("crm", "edit")),
):
    return service.resolve_install_support(customer_id, index, body.get("resolved_by", ""))


@router.patch("/{customer_id}/install-support/{index}", response_model=CustomerInDB)
def edit_install_support(
    customer_id: str,
    index: int,
    body: dict = Body(...),
    _user: TokenPayload = Depends(require_permission("crm", "edit")),
):
    return service.edit_install_support(customer_id, index, body.get("note", ""), body.get("opened_date"))


@router.delete("/{customer_id}/install-support/{index}", response_model=CustomerInDB)
def delete_install_support(
    customer_id: str,
    index: int,
    _user: TokenPayload = Depends(require_permission("crm", "edit")),
):
    return service.delete_install_support(customer_id, index)


# ── Transactions ──────────────────────────────────────────────────────────────

@router.post("/{customer_id}/transactions", response_model=CustomerInDB)
def add_transaction(
    customer_id: str,
    body: TransactionEntry,
    _user: TokenPayload = Depends(require_permission("crm", "edit")),
):
    return service.add_transaction(customer_id, body)


@router.patch("/{customer_id}/transactions/{index}", response_model=CustomerInDB)
def update_transaction(
    customer_id: str,
    index: int,
    body: TransactionEntry,
    _user: TokenPayload = Depends(require_permission("crm", "edit")),
):
    return service.update_transaction(customer_id, index, body)


@router.delete("/{customer_id}/transactions/{index}", response_model=CustomerInDB)
def delete_transaction(
    customer_id: str,
    index: int,
    _user: TokenPayload = Depends(require_permission("crm", "edit")),
):
    return service.delete_transaction(customer_id, index)
