import uuid
from datetime import datetime
from fastapi import APIRouter, Depends, Query, HTTPException
from typing import Optional, List
from pydantic import BaseModel
from sqlalchemy.ext.asyncio import AsyncSession
from auth.models import TokenPayload
from auth.dependencies import require_permission
from devices.models import (
    DeviceCreate, DeviceUpdate, DeviceInDB, DeviceListResponse,
    DeviceUsersResponse, DeviceUserInfo,
    DeviceNoteCreate, DeviceNoteUpdate,
)
from devices import service
import database as mqtt_db
from mqtt.models import DeviceAlertEntry, DeviceAlertsResponse
from shared.firebase import get_db as get_firestore
from database.postgres import get_pg_session
from shared.audit import log_action

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

NOTES_COLLECTION = "notes"
CRM_COLLECTION = "crm_customers"


@router.get("", response_model=DeviceListResponse)
async def list_devices(
    search: Optional[str] = Query(None),
    online: Optional[bool] = Query(None),
    tier: Optional[str] = Query(None),
    _user: TokenPayload = Depends(require_permission("devices", "view")),
):
    devices = service.list_devices(
        search=search,
        online_only=online,
        subscription_tier=tier,
    )
    return DeviceListResponse(devices=devices, total=len(devices))


@router.get("/{device_id}", response_model=DeviceInDB)
async def get_device(
    device_id: str,
    _user: TokenPayload = Depends(require_permission("devices", "view")),
):
    return service.get_device(device_id)


@router.get("/{device_id}/users", response_model=DeviceUsersResponse)
async def get_device_users(
    device_id: str,
    _user: TokenPayload = Depends(require_permission("devices", "view")),
):
    users_data = service.get_device_users(device_id)
    users = [DeviceUserInfo(**u) for u in users_data]
    return DeviceUsersResponse(users=users, total=len(users))


@router.post("", response_model=DeviceInDB, status_code=201)
async def create_device(
    body: DeviceCreate,
    _user: TokenPayload = Depends(require_permission("devices", "add")),
    db: AsyncSession = Depends(get_pg_session),
):
    device = service.create_device(body)
    await log_action(db, _user.sub, _user.name or _user.email, "CREATE", "device",
                     device.device_id, device.device_name or device.device_id)
    return device


@router.put("/{device_id}", response_model=DeviceInDB)
async def update_device(
    device_id: str,
    body: DeviceUpdate,
    _user: TokenPayload = Depends(require_permission("devices", "edit")),
    db: AsyncSession = Depends(get_pg_session),
):
    old = service.get_device(device_id)
    device = service.update_device(device_id, body)
    _SKIP = {"updated_at", "device_id", "tags", "user_list"}
    changes = {
        k: {"old": getattr(old, k, None), "new": getattr(device, k, None)}
        for k in body.model_fields_set
        if k not in _SKIP and getattr(old, k, None) != getattr(device, k, None)
    }
    if "tags" in body.model_fields_set and (old.tags or []) != (device.tags or []):
        changes["tags"] = {"old": sorted(old.tags or []), "new": sorted(device.tags or [])}
    await log_action(db, _user.sub, _user.name or _user.email, "UPDATE", "device",
                     device_id, device.device_name or device_id, changes=changes or None)
    return device


@router.delete("/{device_id}", status_code=204)
async def delete_device(
    device_id: str,
    _user: TokenPayload = Depends(require_permission("devices", "delete")),
    db: AsyncSession = Depends(get_pg_session),
):
    service.delete_device(device_id)
    await log_action(db, _user.sub, _user.name or _user.email, "DELETE", "device",
                     device_id, device_id)


@router.get("/{device_id}/alerts", response_model=DeviceAlertsResponse)
async def get_device_alerts(
    device_id: str,
    _user: TokenPayload = Depends(require_permission("devices", "view")),
):
    """Return the current active alert set for a device. Empty list means fully healthy."""
    rows = await mqtt_db.get_alerts(device_id)
    return DeviceAlertsResponse(alerts=[DeviceAlertEntry(**r) for r in rows])


# ─────────────────────────────────────────────────────────────────────────────
# Device Notes
# ─────────────────────────────────────────────────────────────────────────────

@router.get("/{device_id}/notes")
async def list_device_notes(
    device_id: str,
    _user: TokenPayload = Depends(require_permission("devices", "view")),
):
    """List all notes for a device."""
    db = get_firestore()
    docs = db.collection(NOTES_COLLECTION).where("device_id", "==", device_id).stream()
    notes = []
    for doc in docs:
        note = doc.to_dict()
        note["id"] = doc.id
        for f in ("created_at", "updated_at"):
            if hasattr(note.get(f), "isoformat"):
                note[f] = note[f].isoformat()
        notes.append(note)
    notes.sort(key=lambda n: n.get("created_at") or "", reverse=False)
    return {"notes": notes, "total": len(notes)}


@router.post("/{device_id}/notes", status_code=201)
async def create_device_note(
    device_id: str,
    body: DeviceNoteCreate,
    _user: TokenPayload = Depends(require_permission("devices", "edit")),
):
    """Create a new note for a device."""
    db = get_firestore()
    now = datetime.utcnow()
    note_id = str(uuid.uuid4())
    note_data = {
        "device_id": device_id,
        "content": body.content,
        "created_by": body.created_by or _user.name or "",
        "created_at": now,
        "updated_at": now,
    }
    db.collection(NOTES_COLLECTION).document(note_id).set(note_data)
    note_data["id"] = note_id
    note_data["created_at"] = now.isoformat()
    note_data["updated_at"] = now.isoformat()
    return note_data


@router.put("/{device_id}/notes/{note_id}")
async def update_device_note(
    device_id: str,
    note_id: str,
    body: DeviceNoteUpdate,
    _user: TokenPayload = Depends(require_permission("devices", "edit")),
):
    """Update an existing device note."""
    db = get_firestore()
    doc_ref = db.collection(NOTES_COLLECTION).document(note_id)
    doc = doc_ref.get()
    if not doc.exists or doc.to_dict().get("device_id") != device_id:
        raise HTTPException(status_code=404, detail="Note not found")
    now = datetime.utcnow()
    doc_ref.update({"content": body.content, "updated_at": now})
    updated = doc.to_dict()
    updated["id"] = note_id
    updated["content"] = body.content
    updated["updated_at"] = now.isoformat()
    if hasattr(updated.get("created_at"), "isoformat"):
        updated["created_at"] = updated["created_at"].isoformat()
    return updated


@router.delete("/{device_id}/notes/{note_id}", status_code=204)
async def delete_device_note(
    device_id: str,
    note_id: str,
    _user: TokenPayload = Depends(require_permission("devices", "edit")),
):
    """Delete a device note."""
    db = get_firestore()
    doc_ref = db.collection(NOTES_COLLECTION).document(note_id)
    doc = doc_ref.get()
    if not doc.exists or doc.to_dict().get("device_id") != device_id:
        raise HTTPException(status_code=404, detail="Note not found")
    doc_ref.delete()


# ─────────────────────────────────────────────────────────────────────────────
# Device Tags
# ─────────────────────────────────────────────────────────────────────────────

class TagsUpdate(BaseModel):
    tags: List[str]


@router.put("/{device_id}/tags", response_model=DeviceInDB)
async def update_device_tags(
    device_id: str,
    body: TagsUpdate,
    _user: TokenPayload = Depends(require_permission("devices", "edit")),
):
    """Replace the tags list for a device."""
    return service.update_device(device_id, DeviceUpdate(tags=body.tags))


# ─────────────────────────────────────────────────────────────────────────────
# Assign Device to Customer
# ─────────────────────────────────────────────────────────────────────────────

class CustomerSearchResult(BaseModel):
    id: str
    name: str
    email: str
    organization: str = ""


class AssignCustomerBody(BaseModel):
    customer_id: str
    label: str = ""


@router.get("/{device_id}/customer-search")
async def search_customers_for_device(
    device_id: str,
    q: str = Query(""),
    _user: TokenPayload = Depends(require_permission("devices", "view")),
):
    """Search customers by name, email, phone, org, or tags, returning top 20 matches."""
    db = get_firestore()
    docs = db.collection(CRM_COLLECTION).stream()
    results = []
    q_lower = q.lower().strip()
    for doc in docs:
        data = doc.to_dict()
        name = data.get("name", "") or ""
        surname = data.get("surname", "") or ""
        email = data.get("email", "") or ""
        organization = data.get("organization", "") or ""
        phone = data.get("phone", "") or ""
        tags = " ".join(data.get("tags", []) or [])
        location = data.get("location") or {}
        city = location.get("city", "") or ""
        searchable = f"{name} {surname} {email} {organization} {phone} {tags} {city}".lower()
        if not q_lower or q_lower in searchable:
            results.append({
                "id": doc.id,
                "name": name,
                "surname": surname,
                "email": email,
                "organization": organization,
                "city": city,
            })
        if len(results) >= 20:
            break
    return {"results": results}


@router.post("/{device_id}/assign-customer")
async def assign_device_to_customer(
    device_id: str,
    body: AssignCustomerBody,
    _user: TokenPayload = Depends(require_permission("devices", "edit")),
    db: AsyncSession = Depends(get_pg_session),
):
    """Assign a device to a customer.

    - Sets owner field on the device document.
    - Adds a console_device entry to the customer's owned_items list.
    """
    db = get_firestore()

    # Verify device exists
    device = service.get_device(device_id)

    # Get customer
    customer_ref = db.collection(CRM_COLLECTION).document(body.customer_id)
    customer_doc = customer_ref.get()
    if not customer_doc.exists:
        raise HTTPException(status_code=404, detail="Customer not found")
    customer_data = customer_doc.to_dict()
    customer_email = customer_data.get("email", "")

    # Update device: owner email + customer_id
    device_ref = db.collection("devices").document(device_id)
    device_ref.update({"owner": customer_email, "customer_id": body.customer_id})

    # Add to customer owned_items (avoid duplicates)
    owned_items = customer_data.get("owned_items", []) or []
    already_assigned = any(
        item.get("type") == "console_device" and item.get("console_device", {}).get("device_id") == device_id
        for item in owned_items
    )
    if not already_assigned:
        owned_items.append({
            "type": "console_device",
            "console_device": {
                "device_id": device_id,
                "label": body.label or device.device_name or device_id,
            }
        })
        customer_ref.update({"owned_items": owned_items})

    await log_action(db, _user.sub, _user.name or _user.email, "UPDATE", "device",
                     device_id, device_id, meta={"action_detail": "assigned_to_customer",
                                                  "customer_id": body.customer_id})
    return {"status": "assigned", "device_id": device_id, "customer_id": body.customer_id}


@router.delete("/{device_id}/assign-customer", status_code=204)
async def unassign_device_from_customer(
    device_id: str,
    customer_id: str = Query(...),
    _user: TokenPayload = Depends(require_permission("devices", "edit")),
    db: AsyncSession = Depends(get_pg_session),
):
    """Remove device assignment from a customer."""
    db = get_firestore()

    # Clear customer_id on device
    device_ref = db.collection("devices").document(device_id)
    device_ref.update({"customer_id": ""})

    # Remove from customer owned_items
    customer_ref = db.collection(CRM_COLLECTION).document(customer_id)
    customer_doc = customer_ref.get()
    if customer_doc.exists:
        customer_data = customer_doc.to_dict()
        owned_items = [
            item for item in (customer_data.get("owned_items") or [])
            if not (item.get("type") == "console_device" and item.get("console_device", {}).get("device_id") == device_id)
        ]
        customer_ref.update({"owned_items": owned_items})

    await log_action(db, _user.sub, _user.name or _user.email, "UPDATE", "device",
                     device_id, device_id, meta={"action_detail": "unassigned_from_customer",
                                                  "customer_id": customer_id})


# ─────────────────────────────────────────────────────────────────────────────
# Customer detail (for Owner display in fleet)
# ─────────────────────────────────────────────────────────────────────────────

@router.get("/{device_id}/customer")
async def get_device_customer(
    device_id: str,
    _user: TokenPayload = Depends(require_permission("devices", "view")),
):
    """Return basic customer details for a device's assigned customer_id."""
    db = get_firestore()
    device_ref = db.collection("devices").document(device_id)
    device_doc = device_ref.get()
    if not device_doc.exists:
        raise HTTPException(status_code=404, detail="Device not found")
    device_data = device_doc.to_dict() or {}
    customer_id = device_data.get("customer_id")
    if not customer_id:
        return {"customer": None}
    customer_doc = db.collection(CRM_COLLECTION).document(customer_id).get()
    if not customer_doc.exists:
        return {"customer": None}
    cd = customer_doc.to_dict() or {}
    return {
        "customer": {
            "id": customer_doc.id,
            "name": cd.get("name") or "",
            "email": cd.get("email") or "",
            "organization": cd.get("organization") or "",
            "phone": cd.get("phone") or "",
        }
    }


# ─────────────────────────────────────────────────────────────────────────────
# User list management (for Manage tab — assign/remove users from user_list)
# ─────────────────────────────────────────────────────────────────────────────

class UserSearchResult(BaseModel):
    id: str
    display_name: str = ""
    email: str = ""
    phone: str = ""
    photo_url: str = ""


@router.get("/{device_id}/user-search")
async def search_users_for_device(
    device_id: str,
    q: str = Query(""),
    _user: TokenPayload = Depends(require_permission("devices", "view")),
):
    """Search the users collection by name, email, or phone."""
    db = get_firestore()
    docs = db.collection("users").stream()
    results = []
    q_lower = q.lower().strip()
    for doc in docs:
        data = doc.to_dict() or {}
        name = (data.get("display_name") or "").lower()
        email = (data.get("email") or "").lower()
        phone = (data.get("phone") or "").lower()
        if not q_lower or q_lower in name or q_lower in email or q_lower in phone:
            results.append({
                "id": doc.id,
                "display_name": data.get("display_name") or "",
                "email": data.get("email") or "",
                "phone": data.get("phone") or "",
                "photo_url": data.get("photo_url") or "",
            })
        if len(results) >= 20:
            break
    return {"results": results}


class AddUserBody(BaseModel):
    user_id: str


@router.post("/{device_id}/user-list", status_code=200)
async def add_user_to_device(
    device_id: str,
    body: AddUserBody,
    _user: TokenPayload = Depends(require_permission("devices", "edit")),
    db: AsyncSession = Depends(get_pg_session),
):
    """Add a user reference to the device's user_list field."""
    db = get_firestore()
    device_ref = db.collection("devices").document(device_id)
    device_doc = device_ref.get()
    if not device_doc.exists:
        raise HTTPException(status_code=404, detail="Device not found")

    # Verify user exists
    user_doc = db.collection("users").document(body.user_id).get()
    if not user_doc.exists:
        raise HTTPException(status_code=404, detail="User not found")

    data = device_doc.to_dict() or {}
    user_list = data.get("user_list", []) or []

    # Avoid duplicates — check both string paths and DocumentReferences
    from google.cloud.firestore_v1 import DocumentReference as DocRef
    existing_ids = set()
    for entry in user_list:
        if isinstance(entry, DocRef):
            existing_ids.add(entry.id)
        elif isinstance(entry, str):
            existing_ids.add(entry.split("/")[-1])

    if body.user_id not in existing_ids:
        user_ref = db.collection("users").document(body.user_id)
        user_list.append(user_ref)
        device_ref.update({"user_list": user_list})

    await log_action(db, _user.sub, _user.name or _user.email, "UPDATE", "device",
                     device_id, device_id, meta={"action_detail": "user_added",
                                                  "user_id": body.user_id})
    return {"status": "added", "user_id": body.user_id}


@router.delete("/{device_id}/user-list/{user_id}", status_code=200)
async def remove_user_from_device(
    device_id: str,
    user_id: str,
    _user: TokenPayload = Depends(require_permission("devices", "edit")),
    db: AsyncSession = Depends(get_pg_session),
):
    """Remove a user reference from the device's user_list field."""
    db = get_firestore()
    device_ref = db.collection("devices").document(device_id)
    device_doc = device_ref.get()
    if not device_doc.exists:
        raise HTTPException(status_code=404, detail="Device not found")

    data = device_doc.to_dict() or {}
    user_list = data.get("user_list", []) or []

    from google.cloud.firestore_v1 import DocumentReference as DocRef

    def resolves_to(entry, uid: str) -> bool:
        if isinstance(entry, DocRef):
            return entry.id == uid
        if isinstance(entry, str):
            return entry.split("/")[-1] == uid
        return False

    # Remove any entry that resolves to this user_id (handles both DocRef and string paths)
    new_list = [entry for entry in user_list if not resolves_to(entry, user_id)]
    device_ref.update({"user_list": new_list})

    await log_action(db, _user.sub, _user.name or _user.email, "UPDATE", "device",
                     device_id, device_id, meta={"action_detail": "user_removed",
                                                  "user_id": user_id})
    return {"status": "removed", "user_id": user_id}
