"""Contact folder service — CRUD, tree building, move contacts."""

from __future__ import annotations

import uuid
from typing import Any

from sqlalchemy import func, select, update
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload

from app.models.contact import Contact
from app.models.contact_folder import ContactFolder


def _serialize_folder(f: ContactFolder, contact_count: int = 0) -> dict:
    return {
        "id": str(f.id),
        "name": f.name,
        "parent_id": str(f.parent_id) if f.parent_id else None,
        "user_id": str(f.user_id),
        "sort_order": f.sort_order,
        "contact_count": contact_count,
    }


async def list_folders(
    db: AsyncSession, tenant_id: uuid.UUID, user_id: uuid.UUID
) -> list[dict]:
    """List all folders for a user (flat list, frontend builds tree)."""
    result = await db.execute(
        select(ContactFolder)
        .where(ContactFolder.tenant_id == tenant_id)
        .where(ContactFolder.user_id == user_id)
        .where(ContactFolder.deleted_at.is_(None))
        .order_by(ContactFolder.parent_id, ContactFolder.sort_order, ContactFolder.name)
    )
    folders = result.scalars().all()

    # Count contacts per folder
    folder_ids = [f.id for f in folders]
    counts: dict[uuid.UUID, int] = {}
    if folder_ids:
        count_result = await db.execute(
            select(Contact.folder_id, func.count(Contact.id))
            .where(Contact.folder_id.in_(folder_ids))
            .where(Contact.deleted_at.is_(None))
            .group_by(Contact.folder_id)
        )
        for fid, cnt in count_result:
            counts[fid] = cnt

    return [
        {**_serialize_folder(f), "contact_count": counts.get(f.id, 0)}
        for f in folders
    ]


async def create_folder(
    db: AsyncSession,
    tenant_id: uuid.UUID,
    user_id: uuid.UUID,
    name: str,
    parent_id: str | None = None,
) -> dict:
    """Create a new contact folder."""
    parent_uuid = uuid.UUID(parent_id) if parent_id else None

    # Determine sort_order: max + 1 among siblings
    max_order_result = await db.execute(
        select(func.max(ContactFolder.sort_order))
        .where(ContactFolder.tenant_id == tenant_id)
        .where(ContactFolder.user_id == user_id)
        .where(ContactFolder.parent_id == parent_uuid if parent_uuid else ContactFolder.parent_id.is_(None))
    )
    max_order = max_order_result.scalar() or 0

    folder = ContactFolder(
        tenant_id=tenant_id,
        user_id=user_id,
        name=name,
        parent_id=parent_uuid,
        sort_order=max_order + 1,
    )
    db.add(folder)
    await db.commit()
    await db.refresh(folder)
    return _serialize_folder(folder)


async def update_folder(
    db: AsyncSession,
    tenant_id: uuid.UUID,
    user_id: uuid.UUID,
    folder_id: str,
    data: dict,
) -> dict:
    """Update a contact folder (name, parent_id, sort_order)."""
    result = await db.execute(
        select(ContactFolder)
        .where(ContactFolder.id == uuid.UUID(folder_id))
        .where(ContactFolder.tenant_id == tenant_id)
        .where(ContactFolder.user_id == user_id)
        .where(ContactFolder.deleted_at.is_(None))
    )
    folder = result.scalar_one_or_none()
    if not folder:
        raise ValueError("Folder not found")

    if "name" in data and data["name"]:
        folder.name = data["name"]
    if "parent_id" in data:
        new_parent = data["parent_id"]
        folder.parent_id = uuid.UUID(new_parent) if new_parent else None
    if "sort_order" in data and data["sort_order"] is not None:
        folder.sort_order = data["sort_order"]

    await db.commit()
    await db.refresh(folder)
    return _serialize_folder(folder)


async def delete_folder(
    db: AsyncSession,
    tenant_id: uuid.UUID,
    user_id: uuid.UUID,
    folder_id: str,
) -> None:
    """Delete a contact folder. Contacts are unassigned (folder_id SET NULL)."""
    result = await db.execute(
        select(ContactFolder)
        .where(ContactFolder.id == uuid.UUID(folder_id))
        .where(ContactFolder.tenant_id == tenant_id)
        .where(ContactFolder.user_id == user_id)
        .where(ContactFolder.deleted_at.is_(None))
    )
    folder = result.scalar_one_or_none()
    if not folder:
        raise ValueError("Folder not found")

    # Unassign contacts in this folder (and subfolders via CASCADE on folder delete)
    await db.execute(
        update(Contact)
        .where(Contact.folder_id == folder.id)
        .values(folder_id=None)
    )

    await db.delete(folder)
    await db.commit()


async def move_contact(
    db: AsyncSession,
    tenant_id: uuid.UUID,
    contact_id: str,
    folder_id: str | None,
) -> dict:
    """Move a contact to a folder (or unassign with folder_id=None)."""
    result = await db.execute(
        select(Contact)
        .where(Contact.id == uuid.UUID(contact_id))
        .where(Contact.tenant_id == tenant_id)
        .where(Contact.deleted_at.is_(None))
    )
    contact = result.scalar_one_or_none()
    if not contact:
        raise ValueError("Contact not found")

    contact.folder_id = uuid.UUID(folder_id) if folder_id else None
    await db.commit()
    await db.refresh(contact)
    return {"id": str(contact.id), "folder_id": str(contact.folder_id) if contact.folder_id else None}


async def reorder_folders(
    db: AsyncSession,
    tenant_id: uuid.UUID,
    user_id: uuid.UUID,
    folder_orders: list[dict],
) -> None:
    """Batch update sort_order for folders. Each item: {id, sort_order, parent_id?}."""
    for item in folder_orders:
        fid = uuid.UUID(item["id"])
        await db.execute(
            update(ContactFolder)
            .where(ContactFolder.id == fid)
            .where(ContactFolder.tenant_id == tenant_id)
            .where(ContactFolder.user_id == user_id)
            .values(
                sort_order=item.get("sort_order", 0),
                parent_id=uuid.UUID(item["parent_id"]) if item.get("parent_id") else None,
            )
        )
    await db.commit()
