from __future__ import annotations

from firebase_admin import firestore as admin_firestore

import hashlib
import secrets
from dataclasses import dataclass
from datetime import datetime, timezone
from typing import Dict, List, Optional

from google.cloud.firestore import Client

from app.services.exceptions import ForbiddenError, NotFoundError, ValidationError
from app.services.firestore import get_firestore_client

COLLECTION_NAME = "apiKeys"
KEY_PREFIX_LENGTH = 8


@dataclass
class ApiKeyValidationResult:
    valid: bool
    user_id: Optional[str] = None
    key_id: Optional[str] = None
    message: Optional[str] = None


def _hash_key(raw_key: str) -> str:
    return hashlib.sha256(raw_key.encode("utf-8")).hexdigest()


def _generate_raw_key() -> str:
    return f"tano_{secrets.token_hex(16)}"


def _collection(db: Client):
    return db.collection(COLLECTION_NAME)


def generate_api_key(user_id: str, name: str, description: Optional[str] = None) -> Dict:
    if not name.strip():
        raise ValidationError("API key name is required")

    db = get_firestore_client()
    raw_key = _generate_raw_key()
    hashed_key = _hash_key(raw_key)
    prefix = raw_key[:KEY_PREFIX_LENGTH]
    now = datetime.now(timezone.utc)

    doc_ref = _collection(db).document()
    record = {
        "userId": user_id,
        "name": name.strip(),
        "description": description.strip() if description else "",
        "hashedKey": hashed_key,
        "keyPrefix": prefix,
        "createdAt": now,
        "lastUsed": None,
        "usageCount": 0,
        "active": True,
    }
    doc_ref.set(record)

    response = {
        "key": raw_key,
        "record": {
            "id": doc_ref.id,
            "name": record["name"],
            "description": record["description"],
            "createdAt": now,
            "lastUsed": None,
            "usageCount": 0,
            "active": True,
        },
    }
    return response


def list_api_keys(user_id: str) -> List[Dict]:
    db = get_firestore_client()
    query = _collection(db).where("userId", "==", user_id).order_by("createdAt", direction=admin_firestore.Query.DESCENDING)

    results: List[Dict] = []
    for doc in query.stream():
        data = doc.to_dict()
        results.append(
            {
                "id": doc.id,
                "name": data.get("name"),
                "description": data.get("description"),
                "createdAt": _to_datetime(data.get("createdAt")),
                "lastUsed": _to_datetime(data.get("lastUsed")),
                "usageCount": data.get("usageCount", 0),
                "active": data.get("active", True),
            }
        )
    return results


def toggle_api_key(user_id: str, key_id: str, active: bool) -> None:
    db = get_firestore_client()
    doc_ref = _collection(db).document(key_id)
    snapshot = doc_ref.get()

    if not snapshot.exists:
        raise NotFoundError("API key not found")

    data = snapshot.to_dict()
    if data.get("userId") != user_id:
        raise ForbiddenError("Cannot modify this API key")

    doc_ref.update({
        "active": active,
        "updatedAt": datetime.now(timezone.utc),
    })


def revoke_api_key(user_id: str, key_id: str) -> None:
    db = get_firestore_client()
    doc_ref = _collection(db).document(key_id)
    snapshot = doc_ref.get()

    if not snapshot.exists:
        raise NotFoundError("API key not found")

    data = snapshot.to_dict()
    if data.get("userId") != user_id:
        raise ForbiddenError("Cannot revoke this API key")

    doc_ref.delete()


def validate_api_key(raw_key: str) -> ApiKeyValidationResult:
    hashed_key = _hash_key(raw_key)
    db = get_firestore_client()
    query = _collection(db).where("hashedKey", "==", hashed_key).where("active", "==", True)
    docs = list(query.stream())

    if not docs:
        return ApiKeyValidationResult(valid=False, message="Invalid or inactive API key")

    doc = docs[0]
    data = doc.to_dict()

    doc.reference.update(
        {
            "lastUsed": datetime.now(timezone.utc),
            "usageCount": data.get("usageCount", 0) + 1,
        }
    )

    return ApiKeyValidationResult(valid=True, user_id=data.get("userId"), key_id=doc.id)


def _to_datetime(value):
    if value is None:
        return None
    if hasattr(value, "to_datetime"):
        return value.to_datetime()
    return value
