"""Firebase authentication and Firestore synchronization helpers."""
from __future__ import annotations

import time
from dataclasses import dataclass
from typing import Dict, Optional

import requests

FIREBASE_CONFIG = {
    "apiKey": "AIzaSyAkxOXZdtpA3M-Y7w_4r3-p_mwU0WAV1T0",
    "authDomain": "erosolar-encryption-b2c60.firebaseapp.com",
    "projectId": "erosolar-encryption-b2c60",
    "storageBucket": "erosolar-encryption-b2c60.firebasestorage.app",
    "messagingSenderId": "679177387857",
    "appId": "1:679177387857:web:379cccb9134b513f3a9d70",
    "measurementId": "G-56W2SR5ETF",
}

API_KEY = FIREBASE_CONFIG["apiKey"]
PROJECT_ID = FIREBASE_CONFIG["projectId"]

IDENTITY_BASE = "https://identitytoolkit.googleapis.com/v1"
FIRESTORE_BASE = f"https://firestore.googleapis.com/v1/projects/{PROJECT_ID}/databases/(default)/documents"


class FirebaseAuthError(RuntimeError):
    """Raised when Firebase authentication fails."""


class EmailNotVerifiedError(FirebaseAuthError):
    """Raised when the Firebase user has not verified their email address yet."""

    def __init__(self, message: str, session: "FirebaseSession"):
        super().__init__(message)
        self.session = session


@dataclass
class FirebaseSession:
    id_token: str
    refresh_token: str
    user_id: str
    email: str
    expiration: float

    def ensure_valid(self) -> None:
        if time.time() < self.expiration - 30:
            return
        raise FirebaseAuthError("Session token expired; re-authentication required.")


def _post(url: str, payload: Dict[str, object]) -> Dict[str, object]:
    response = requests.post(url, json=payload, timeout=15)
    if response.status_code != 200:
        raise FirebaseAuthError(response.text)
    return response.json()


def _request(method: str, url: str, session: FirebaseSession, payload: Optional[Dict[str, object]] = None) -> Dict[str, object]:
    session.ensure_valid()
    headers = {
        "Authorization": f"Bearer {session.id_token}",
        "Content-Type": "application/json",
    }
    response = requests.request(method, url, headers=headers, json=payload, timeout=15)
    if response.status_code == 404:
        raise FileNotFoundError(url)
    if response.status_code >= 400:
        raise FirebaseAuthError(response.text)
    if response.content:
        return response.json()
    return {}


def _build_session(data: Dict[str, object]) -> FirebaseSession:
    expires_raw = data.get("expiresIn", "3600")
    try:
        expires_in = int(expires_raw)
    except (TypeError, ValueError):
        expires_in = 3600
    return FirebaseSession(
        id_token=str(data["idToken"]),
        refresh_token=str(data["refreshToken"]),
        user_id=str(data["localId"]),
        email=str(data.get("email") or ""),
        expiration=time.time() + expires_in,
    )


def _lookup_user(session: FirebaseSession) -> Dict[str, object]:
    return _post(f"{IDENTITY_BASE}/accounts:lookup?key={API_KEY}", {"idToken": session.id_token})


def send_verification_email(session: FirebaseSession) -> None:
    """Trigger a Firebase verification email for the provided session."""
    _post(
        f"{IDENTITY_BASE}/accounts:sendOobCode?key={API_KEY}",
        {"requestType": "VERIFY_EMAIL", "idToken": session.id_token},
    )


def sign_in_with_email(email: str, password: str) -> FirebaseSession:
    url = f"{IDENTITY_BASE}/accounts:signInWithPassword?key={API_KEY}"
    data = _post(url, {"email": email, "password": password, "returnSecureToken": True})
    session = _build_session(data)
    lookup = _lookup_user(session)
    user_info = lookup.get("users", [{}])[0]
    if not user_info.get("emailVerified"):
        raise EmailNotVerifiedError(
            "Email address is not verified. Please check your inbox for a verification link.",
            session,
        )
    return session


def sign_up_with_email(email: str, password: str) -> FirebaseSession:
    """Create a new Firebase account using email/password credentials."""
    url = f"{IDENTITY_BASE}/accounts:signUp?key={API_KEY}"
    data = _post(url, {"email": email, "password": password, "returnSecureToken": True})
    return _build_session(data)


DEFAULT_REMOTE_STORE = {
    "master_pw_hash": "",
    "private_key": "",
    "sign_private_key": "",
    "credentials": "",
    "pgp_keyvault": "",
    "public_key": "",
    "verify_public_key": "",
    "cards": "",
}


def _firestore_doc_path(user_id: str) -> str:
    return f"{FIRESTORE_BASE}/users/{user_id}"


def fetch_user_store(session: FirebaseSession) -> Dict[str, str]:
    url = _firestore_doc_path(session.user_id)
    try:
        doc = _request("GET", url, session)
    except FileNotFoundError:
        create_user_store(session, DEFAULT_REMOTE_STORE.copy())
        return DEFAULT_REMOTE_STORE.copy()
    fields = doc.get("fields", {})
    store: Dict[str, str] = {}
    for key, value in DEFAULT_REMOTE_STORE.items():
        store[key] = fields.get(key, {}).get("stringValue", value)
    return store


def create_user_store(session: FirebaseSession, store: Dict[str, str]) -> None:
    url = f"{FIRESTORE_BASE}/users?documentId={session.user_id}"
    payload = {"fields": {k: {"stringValue": v or ""} for k, v in store.items()}}
    _request("POST", url, session, payload)


def replace_user_store(session: FirebaseSession, store: Dict[str, str]) -> None:
    url = _firestore_doc_path(session.user_id)
    payload = {"fields": {k: {"stringValue": v or ""} for k, v in store.items()}}
    try:
        _request("PATCH", url, session, payload)
    except FileNotFoundError:
        create_user_store(session, store)


def delete_user_store(session: FirebaseSession) -> None:
    url = _firestore_doc_path(session.user_id)
    _request("DELETE", url, session)
