import sys
sys.path.insert(0, "/var/www/html/metafoliadorv2")

from fastapi import APIRouter, Depends, HTTPException, Query, Path
from sqlalchemy.orm import Session

from app.schemas import CreateConnectionRequest, UpdateConnectionRequest

from app.models.database import get_db
from app.models.alfresco_connection import AlfrescoConnection
from app.models.alfresco_site import AlfrescoSite
from app.services.auth_service import encrypt_credential, decrypt_credential
from app.services.timezone import now as tznow
from app.services.timezone import iso

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


@router.get("", summary="Listar conexiones", description="Lista todas las conexiones Alfresco configuradas. Permite filtrar por activas y búsqueda por nombre.")
async def list_connections(
    active_only: bool = Query(False, description="Filtrar solo conexiones activas"),
    search: str = Query(None, description="Buscar por nombre de conexión"),
    db: Session = Depends(get_db),
):
    q = db.query(AlfrescoConnection)
    if active_only:
        q = q.filter(AlfrescoConnection.active == True)
    if search:
        q = q.filter(AlfrescoConnection.name.ilike(f"%{search}%"))
    connections = q.order_by(AlfrescoConnection.name).all()
    repo_sizes = _get_repo_sizes(db)
    return [_serialize(c, repo_sizes.get(c.id, 0)) for c in connections]


@router.get("/{connection_id}", summary="Obtener conexión", description="Devuelve el detalle completo de una conexión Alfresco por su ID.")
async def get_connection(connection_id: int = Path(..., description="ID de la conexión Alfresco"), db: Session = Depends(get_db)):
    c = db.query(AlfrescoConnection).filter(AlfrescoConnection.id == connection_id).first()
    if not c:
        raise HTTPException(status_code=404, detail="Conexion no encontrada")
    repo_sizes = _get_repo_sizes(db)
    return _serialize(c, repo_sizes.get(c.id, 0))


@router.post("", status_code=201, summary="Crear conexión", description="Registra una nueva conexión a una instancia Alfresco. La contraseña se almacena cifrada con Fernet.")
async def create_connection(body: CreateConnectionRequest, db: Session = Depends(get_db)):
    required = ["name", "base_url", "ip_server"]
    for field in required:
        if not getattr(body, field):
            raise HTTPException(status_code=400, detail=f"{field} es requerido")

    c = AlfrescoConnection(
        name=body.name,
        ip_server=body.ip_server,
        base_url=body.base_url.rstrip("/"),
        username=body.username or "",
        password_hash=encrypt_credential(body.password) if body.password else None,
        api_username=body.api_username or None,
        api_password_hash=encrypt_credential(body.api_password) if body.api_password else None,
        client_id=body.client_id or "",
        client_secret_hash=encrypt_credential(body.client_secret) if body.client_secret else None,
        auth_type=body.auth_type or "basic",
        active=body.active if body.active is not None else True,
        notes=body.notes or "",
    )
    db.add(c)
    db.commit()
    db.refresh(c)
    return _serialize(c)


@router.put("/{connection_id}", summary="Actualizar conexión", description="Modifica los datos de una conexión Alfresco existente. Si se envía contraseña, se re-cifra.")
async def update_connection(connection_id: int = Path(..., description="ID de la conexión Alfresco"), body: UpdateConnectionRequest = None, db: Session = Depends(get_db)):
    c = db.query(AlfrescoConnection).filter(AlfrescoConnection.id == connection_id).first()
    if not c:
        raise HTTPException(status_code=404, detail="Conexion no encontrada")

    data = body.model_dump(exclude_unset=True)
    for field in ["name", "base_url", "ip_server", "username", "api_username", "client_id", "auth_type", "notes"]:
        if field in data and data[field] is not None:
            setattr(c, field, data[field] if field != "base_url" else data[field].rstrip("/"))
    if body.password:
        c.password_hash = encrypt_credential(body.password)
    if body.api_password:
        c.api_password_hash = encrypt_credential(body.api_password)
    if body.client_secret:
        c.client_secret_hash = encrypt_credential(body.client_secret)
    if "active" in data and data["active"] is not None:
        c.active = data["active"]

    db.commit()
    db.refresh(c)
    return _serialize(c)


@router.delete("/{connection_id}", summary="Eliminar conexión", description="Elimina permanentemente una conexión Alfresco y todos sus sitios asociados.")
async def delete_connection(connection_id: int = Path(..., description="ID de la conexión a eliminar"), db: Session = Depends(get_db)):
    c = db.query(AlfrescoConnection).filter(AlfrescoConnection.id == connection_id).first()
    if not c:
        raise HTTPException(status_code=404, detail="Conexion no encontrada")
    db.delete(c)
    db.commit()
    return {"ok": True}


@router.post("/{connection_id}/test", summary="Probar conexión", description="Verifica conectividad con la instancia Alfresco consultando su endpoint de versión. Actualiza last_check_at y last_check_status.")
async def test_connection(connection_id: int = Path(..., description="ID de la conexión a probar"), db: Session = Depends(get_db)):
    c = db.query(AlfrescoConnection).filter(AlfrescoConnection.id == connection_id).first()
    if not c:
        raise HTTPException(status_code=404, detail="Conexion no encontrada")

    import httpx
    try:
        url = f"{c.base_url}/alfresco/api/-default-/public/alfresco/versions/1/version"
        auth = None
        if c.username and c.password_hash:
            pwd = decrypt_credential(c.password_hash)
            auth = httpx.BasicAuth(c.username, pwd)
        async with httpx.AsyncClient(timeout=10, verify=False) as client:
            resp = await client.get(url, auth=auth)
            c.last_check_at = tznow()
            c.last_check_status = "ok" if resp.status_code < 500 else "error"
            db.commit()
            return {"ok": True, "status_code": resp.status_code, "info": resp.json() if resp.status_code == 200 else str(resp.status_code)}
    except Exception as e:
        c.last_check_at = tznow()
        c.last_check_status = "error"
        db.commit()
        return {"ok": False, "error": str(e)}


@router.post("/{connection_id}/fetch-sites", summary="Descubrir sitios desde Alfresco", description="Consulta la API de Alfresco para obtener todos los sitios disponibles y los registra/actualiza en la base de datos local.")
async def fetch_sites_from_alfresco(connection_id: int = Path(..., description="ID de la conexión Alfresco"), db: Session = Depends(get_db)):
    c = db.query(AlfrescoConnection).filter(AlfrescoConnection.id == connection_id).first()
    if not c:
        raise HTTPException(status_code=404, detail="Conexion no encontrada")
    if not c.username or not c.password_hash:
        raise HTTPException(status_code=400, detail="La conexion no tiene credenciales configuradas")
    if not c.active:
        raise HTTPException(status_code=400, detail="La conexion esta inactiva")

    import httpx
    import asyncio

    try:
        pwd = decrypt_credential(c.password_hash)
        auth = httpx.BasicAuth(c.username, pwd)

        async with httpx.AsyncClient(timeout=30, verify=False) as client:
            sites_url = f"{c.base_url}/alfresco/api/-default-/public/alfresco/versions/1/sites?maxItems=1000"
            resp = await client.get(sites_url, auth=auth)

            if resp.status_code == 401:
                raise HTTPException(status_code=401, detail="Credenciales invalidas para Alfresco")
            if resp.status_code != 200:
                raise HTTPException(status_code=502, detail=f"Alfresco respondio HTTP {resp.status_code}")

            data = resp.json()
            entries = data.get("list", {}).get("entries", [])

            created = 0
            updated = 0
            errors = []

            for entry in entries:
                site_info = entry.get("entry", {})
                site_id = site_info.get("id", "")
                if not site_id:
                    continue

                site_data = {
                    "name": site_id,
                    "site_id": site_id,
                    "title": site_info.get("title", site_id),
                    "description": site_info.get("description", ""),
                    "visibility": site_info.get("visibility", "PUBLIC"),
                    "site_preset": site_info.get("preset", ""),
                    "site_role": site_info.get("role", ""),
                }

                try:
                    # 1. Get site detail to extract root folder GUID
                    site_detail_url = f"{c.base_url}/alfresco/api/-default-/public/alfresco/versions/1/sites/{site_id}"
                    detail_resp = await client.get(site_detail_url, auth=auth)
                    if detail_resp.status_code == 200:
                        detail = detail_resp.json().get("entry", {})
                        guid = detail.get("guid", "")
                        if guid:
                            site_data["root_folder_id"] = guid
                            site_data["root_folder_path"] = f"/Sites/{site_id}"

                    # 2. Get documentLibrary folderId via nodes API (more reliable than containers)
                    doclib_id = ""
                    if guid:
                        # Try containers API first
                        containers_url = f"{c.base_url}/alfresco/api/-default-/public/alfresco/versions/1/sites/{site_id}/containers"
                        cont_resp = await client.get(containers_url, auth=auth)
                        if cont_resp.status_code == 200:
                            containers = cont_resp.json().get("list", {}).get("entries", [])
                            for container in containers:
                                cinfo = container.get("entry", {})
                                container_id = cinfo.get("id", "")
                                folder_id = cinfo.get("folderId", "")
                                if container_id == "documentLibrary":
                                    if folder_id:
                                        doclib_id = folder_id
                                    break

                        # Fallback: get documentLibrary via nodes children API
                        if not doclib_id and guid:
                            doclib_url = f"{c.base_url}/alfresco/api/-default-/public/alfresco/versions/1/nodes/{guid}/children?maxItems=20"
                            dl_resp = await client.get(doclib_url, auth=auth)
                            if dl_resp.status_code == 200:
                                children = dl_resp.json().get("list", {}).get("entries", [])
                                for child in children:
                                    child_entry = child.get("entry", {})
                                    if child_entry.get("name") == "documentLibrary" and child_entry.get("isFolder"):
                                        doclib_id = child_entry.get("id", "")
                                        break
                            else:
                                errors.append(f"{site_id}: nodes children HTTP {dl_resp.status_code}")

                    if doclib_id:
                        site_data["document_library_id"] = doclib_id
                        if not site_data.get("root_folder_path"):
                            site_data["root_folder_path"] = f"/Sites/{site_id}/documentLibrary"
                    elif not errors or not any(site_id in e for e in errors):
                        errors.append(f"{site_id}: documentLibrary no encontrado")
                except Exception as e:
                    errors.append(f"{site_id}: error={str(e)}")

                existing = db.query(AlfrescoSite).filter(
                    AlfrescoSite.site_id == site_id,
                    AlfrescoSite.connection_id == connection_id,
                ).first()

                if existing:
                    for key, val in site_data.items():
                        if val:
                            setattr(existing, key, val)
                    existing.last_sync_at = tznow()
                    updated += 1
                else:
                    s = AlfrescoSite(
                        connection_id=connection_id,
                        **site_data,
                        active=False,
                        last_sync_at=tznow(),
                    )
                    db.add(s)
                    created += 1

            db.commit()

            c.last_check_at = tznow()
            c.last_check_status = "ok"
            db.commit()

            return {
                "ok": True,
                "total_sites": len(entries),
                "created": created,
                "updated": updated,
                "errors": errors,
            }

    except HTTPException:
        raise
    except Exception as e:
        c.last_check_at = tznow()
        c.last_check_status = "error"
        db.commit()
        raise HTTPException(status_code=500, detail=str(e))


def _format_size(bytes_val):
    if not bytes_val or bytes_val == 0:
        return "—"
    if bytes_val < 1024:
        return f"{bytes_val} B"
    elif bytes_val < 1048576:
        return f"{bytes_val / 1024:.1f} KB"
    elif bytes_val < 1073741824:
        return f"{bytes_val / 1048576:.1f} MB"
    elif bytes_val < 1099511627776:
        return f"{bytes_val / 1073741824:.2f} GB"
    else:
        return f"{bytes_val / 1099511627776:.2f} TB"


def _get_repo_sizes(db) -> dict:
    """Retorna {connection_id: total_size_bytes} sumando size_bytes de todos los archivos."""
    from sqlalchemy import text
    rows = db.execute(text("""
        SELECT s.connection_id, COALESCE(SUM(afi.size_bytes), 0)
        FROM alfresco_sites s
        JOIN alfresco_folders af ON af.site_id = s.id AND af.deleted = false
        JOIN alfresco_files afi ON afi.folder_id = af.id AND afi.deleted = false AND afi.name != 'index.xml'
        GROUP BY s.connection_id
    """)).fetchall()
    return {r[0]: r[1] for r in rows}


def _serialize(c: AlfrescoConnection, repo_size: int = 0) -> dict:
    return {
        "id": c.id,
        "name": c.name,
        "base_url": c.base_url,
        "ip_server": c.ip_server,
        "username": c.username,
        "has_password": bool(c.password_hash),
        "api_username": c.api_username,
        "has_api_password": bool(c.api_password_hash),
        "client_id": c.client_id,
        "has_client_secret": bool(c.client_secret_hash),
        "auth_type": c.auth_type,
        "active": c.active,
        "last_check_at": iso(c.last_check_at),
        "last_check_status": c.last_check_status,
        "notes": c.notes,
        "sites_count": len(c.sites) if c.sites else 0,
        "repo_size": repo_size,
        "repo_size_human": _format_size(repo_size),
        "created_at": iso(c.created_at),
        "updated_at": iso(c.updated_at),
    }
