from __future__ import annotations

import json
from urllib.parse import quote

import httpx

GOOGLE_DRIVE_ENDPOINT = "https://www.googleapis.com/drive/v3"


def _escape_drive_query_value(value: str) -> str:
    return value.replace("\\", "\\\\").replace("'", "\\'")


async def _lookup_folder_by_reference(
    client: httpx.AsyncClient,
    *,
    access_token: str,
    folder_reference: str,
) -> str | None:
    response = await client.get(
        f"{GOOGLE_DRIVE_ENDPOINT}/files/{quote(folder_reference, safe='')}",
        headers={"Authorization": f"Bearer {access_token}"},
        params={
            "fields": "id, name, mimeType",
            "supportsAllDrives": "true",
        },
    )
    if response.status_code == 200:
        payload = response.json()
        if payload.get("mimeType") != "application/vnd.google-apps.folder":
            raise ValueError("Il riferimento Drive esiste ma non punta a una cartella.")
        return str(payload["id"])
    if response.status_code not in {400, 403, 404}:
        response.raise_for_status()
    return None


async def _find_drive_folder(
    client: httpx.AsyncClient,
    *,
    access_token: str,
    folder_name: str,
    parent_id: str | None,
    search_anywhere: bool = False,
) -> str | None:
    query_parts = [
        "mimeType = 'application/vnd.google-apps.folder'",
        "trashed = false",
        f"name = '{_escape_drive_query_value(folder_name)}'",
    ]
    if parent_id:
        query_parts.append(f"'{parent_id}' in parents")
    elif not search_anywhere:
        query_parts.append("'root' in parents")

    response = await client.get(
        f"{GOOGLE_DRIVE_ENDPOINT}/files",
        headers={"Authorization": f"Bearer {access_token}"},
        params={
            "q": " and ".join(query_parts),
            "pageSize": 1,
            "orderBy": "createdTime asc",
            "fields": "files(id, name, mimeType, createdTime)",
            "supportsAllDrives": "true",
            "includeItemsFromAllDrives": "true",
        },
    )
    response.raise_for_status()
    files = response.json().get("files", [])
    if isinstance(files, list) and files:
        first = files[0]
        if isinstance(first, dict) and first.get("id"):
            return str(first["id"])
    return None


async def _create_drive_folder(
    client: httpx.AsyncClient,
    *,
    access_token: str,
    folder_name: str,
    parent_id: str | None,
) -> str:
    payload: dict[str, object] = {
        "name": folder_name,
        "mimeType": "application/vnd.google-apps.folder",
    }
    if parent_id:
        payload["parents"] = [parent_id]
    response = await client.post(
        f"{GOOGLE_DRIVE_ENDPOINT}/files",
        headers={"Authorization": f"Bearer {access_token}"},
        params={
            "fields": "id, name",
            "supportsAllDrives": "true",
        },
        json=payload,
    )
    response.raise_for_status()
    return str(response.json()["id"])


async def resolve_or_create_drive_folder(
    client: httpx.AsyncClient,
    *,
    access_token: str,
    folder_reference: str,
) -> str:
    normalized_reference = folder_reference.strip().strip("/")
    if not normalized_reference:
        raise ValueError("Nome cartella Drive non valido.")

    if "/" not in normalized_reference:
        existing = await _lookup_folder_by_reference(
            client,
            access_token=access_token,
            folder_reference=normalized_reference,
        )
        if existing:
            return existing

    segments = [segment.strip() for segment in normalized_reference.split("/") if segment.strip()]
    if not segments:
        raise ValueError("Nome cartella Drive non valido.")

    parent_id: str | None = None
    for index, segment in enumerate(segments):
        existing_folder = await _find_drive_folder(
            client,
            access_token=access_token,
            folder_name=segment,
            parent_id=parent_id,
        )
        if existing_folder is None and parent_id is None and index == 0:
            existing_folder = await _find_drive_folder(
                client,
                access_token=access_token,
                folder_name=segment,
                parent_id=None,
                search_anywhere=True,
            )
        if existing_folder:
            parent_id = existing_folder
            continue
        parent_id = await _create_drive_folder(
            client,
            access_token=access_token,
            folder_name=segment,
            parent_id=parent_id,
        )

    if not parent_id:
        raise ValueError("Nome cartella Drive non valido.")
    return parent_id


async def _lookup_drive_file(
    client: httpx.AsyncClient,
    *,
    access_token: str,
    file_id: str,
) -> dict[str, str] | None:
    response = await client.get(
        f"{GOOGLE_DRIVE_ENDPOINT}/files/{quote(file_id, safe='')}",
        headers={"Authorization": f"Bearer {access_token}"},
        params={
            "fields": "id, name, webViewLink, parents",
            "supportsAllDrives": "true",
        },
    )
    if response.status_code == 404:
        return None
    response.raise_for_status()
    payload = response.json()
    return {
        "file_id": str(payload["id"]),
        "web_url": str(payload.get("webViewLink") or f"https://drive.google.com/file/d/{payload['id']}/view"),
    }


async def _find_drive_file_by_name(
    client: httpx.AsyncClient,
    *,
    access_token: str,
    folder_id: str,
    filename: str,
) -> dict[str, str] | None:
    response = await client.get(
        f"{GOOGLE_DRIVE_ENDPOINT}/files",
        headers={"Authorization": f"Bearer {access_token}"},
        params={
            "q": (
                "trashed = false "
                f"and name = '{_escape_drive_query_value(filename)}' "
                f"and '{folder_id}' in parents"
            ),
            "pageSize": 1,
            "orderBy": "createdTime desc",
            "fields": "files(id, name, webViewLink, parents)",
            "supportsAllDrives": "true",
            "includeItemsFromAllDrives": "true",
        },
    )
    response.raise_for_status()
    files = response.json().get("files", [])
    if isinstance(files, list) and files:
        first = files[0]
        if isinstance(first, dict) and first.get("id"):
            return {
                "file_id": str(first["id"]),
                "web_url": str(first.get("webViewLink") or f"https://drive.google.com/file/d/{first['id']}/view"),
            }
    return None


async def _move_file_to_folder(
    client: httpx.AsyncClient,
    *,
    access_token: str,
    file_id: str,
    folder_id: str,
    filename: str | None = None,
) -> None:
    headers = {"Authorization": f"Bearer {access_token}"}
    metadata_response = await client.get(
        f"{GOOGLE_DRIVE_ENDPOINT}/files/{file_id}",
        headers=headers,
        params={
            "fields": "parents",
            "supportsAllDrives": "true",
        },
    )
    metadata_response.raise_for_status()
    parents = metadata_response.json().get("parents", [])
    params = {
        "addParents": folder_id,
        "fields": "id, parents",
        "supportsAllDrives": "true",
    }
    if parents:
        params["removeParents"] = ",".join(str(item) for item in parents if str(item).strip())
    response = await client.patch(
        f"{GOOGLE_DRIVE_ENDPOINT}/files/{file_id}",
        headers=headers,
        params=params,
        json={"name": filename} if filename else None,
    )
    response.raise_for_status()


async def upsert_text_file_to_drive(
    client: httpx.AsyncClient,
    *,
    access_token: str,
    filename: str,
    text_content: str,
    folder_reference: str,
    existing_file_id: str | None = None,
) -> dict[str, str]:
    folder_id = await resolve_or_create_drive_folder(
        client,
        access_token=access_token,
        folder_reference=folder_reference,
    )
    raw_bytes = text_content.encode("utf-8")
    target_file_id = existing_file_id

    if target_file_id:
        existing = await _lookup_drive_file(
            client,
            access_token=access_token,
            file_id=target_file_id,
        )
        if existing is None:
            target_file_id = None

    if not target_file_id:
        existing_by_name = await _find_drive_file_by_name(
            client,
            access_token=access_token,
            folder_id=folder_id,
            filename=filename,
        )
        if existing_by_name:
            target_file_id = existing_by_name["file_id"]

    if target_file_id:
        await _move_file_to_folder(
            client,
            access_token=access_token,
            file_id=target_file_id,
            folder_id=folder_id,
            filename=filename,
        )
        upload_response = await client.patch(
            f"https://www.googleapis.com/upload/drive/v3/files/{quote(target_file_id, safe='')}",
            headers={
                "Authorization": f"Bearer {access_token}",
                "Content-Type": "text/plain; charset=UTF-8",
            },
            params={"uploadType": "media", "supportsAllDrives": "true"},
            content=raw_bytes,
        )
        upload_response.raise_for_status()
        payload = await _lookup_drive_file(
            client,
            access_token=access_token,
            file_id=target_file_id,
        )
        if payload is None:
            raise ValueError("File Drive aggiornato ma non piu disponibile.")
        return {
            "file_id": payload["file_id"],
            "web_url": payload["web_url"],
            "folder_id": folder_id,
        }

    metadata = {"name": filename, "parents": [folder_id]}
    files = {
        "metadata": ("metadata", json.dumps(metadata), "application/json; charset=UTF-8"),
        "file": (filename, raw_bytes, "text/plain; charset=UTF-8"),
    }
    response = await client.post(
        "https://www.googleapis.com/upload/drive/v3/files",
        headers={"Authorization": f"Bearer {access_token}"},
        params={
            "uploadType": "multipart",
            "fields": "id, webViewLink, parents",
            "supportsAllDrives": "true",
        },
        files=files,
    )
    response.raise_for_status()
    payload = response.json()
    return {
        "file_id": str(payload["id"]),
        "web_url": str(payload.get("webViewLink") or f"https://drive.google.com/file/d/{payload['id']}/view"),
        "folder_id": folder_id,
    }


async def upload_binary_file_to_drive(
    client: httpx.AsyncClient,
    *,
    access_token: str,
    filename: str,
    mime_type: str,
    raw_bytes: bytes,
    folder_reference: str,
) -> dict[str, str]:
    folder_id = await resolve_or_create_drive_folder(
        client,
        access_token=access_token,
        folder_reference=folder_reference,
    )
    metadata = {"name": filename, "parents": [folder_id]}
    files = {
        "metadata": ("metadata", json.dumps(metadata), "application/json; charset=UTF-8"),
        "file": (filename, raw_bytes, mime_type),
    }
    response = await client.post(
        "https://www.googleapis.com/upload/drive/v3/files",
        headers={"Authorization": f"Bearer {access_token}"},
        params={
            "uploadType": "multipart",
            "fields": "id, webViewLink, parents",
            "supportsAllDrives": "true",
        },
        files=files,
    )
    response.raise_for_status()
    payload = response.json()
    return {
        "file_id": str(payload["id"]),
        "web_url": str(payload.get("webViewLink") or f"https://drive.google.com/file/d/{payload['id']}/view"),
        "folder_id": folder_id,
    }
