"""Firestore storage utilities for console data."""

import secrets
import bcrypt
from typing import Any, Dict, List, Optional
from datetime import datetime
from google.cloud import firestore
from agenticlypay.config import config

# Initialize Firestore client
db = firestore.Client(project=config.project_id or "agenticlypay-com")


class FirestoreStorage:
    """Firestore-based storage for console data."""

    @staticmethod
    def get_collection(name: str):
        """Get a Firestore collection reference."""
        return db.collection(name)

    @staticmethod
    def create_project(user_id: str, name: str) -> Dict[str, Any]:
        """Create a new project."""
        project_ref = db.collection("projects").document()
        project_data = {
            "id": project_ref.id,
            "user_id": user_id,
            "name": name,
            "created_at": firestore.SERVER_TIMESTAMP,
            "updated_at": firestore.SERVER_TIMESTAMP,
        }
        project_ref.set(project_data)
        return {**project_data, "created_at": datetime.now().isoformat(), "updated_at": datetime.now().isoformat()}

    @staticmethod
    def get_projects(user_id: str) -> List[Dict[str, Any]]:
        """Get all projects for a user."""
        projects = db.collection("projects").where("user_id", "==", user_id).stream()
        result = []
        for doc in projects:
            data = doc.to_dict()
            if data:
                # Convert Firestore timestamps to ISO strings
                if "created_at" in data and hasattr(data["created_at"], "isoformat"):
                    data["created_at"] = data["created_at"].isoformat()
                if "updated_at" in data and hasattr(data["updated_at"], "isoformat"):
                    data["updated_at"] = data["updated_at"].isoformat()
                result.append(data)
        return result

    @staticmethod
    def get_project(project_id: str) -> Optional[Dict[str, Any]]:
        """Get a project by ID."""
        doc = db.collection("projects").document(project_id).get()
        if doc.exists:
            data = doc.to_dict()
            if data:
                if "created_at" in data and hasattr(data["created_at"], "isoformat"):
                    data["created_at"] = data["created_at"].isoformat()
                if "updated_at" in data and hasattr(data["updated_at"], "isoformat"):
                    data["updated_at"] = data["updated_at"].isoformat()
            return data
        return None

    @staticmethod
    def log_usage(email: str, endpoint: str, status_code: int) -> None:
        """Log API usage by email."""
        usage_ref = db.collection("usage").document()
        usage_data = {
            "email": email,
            "endpoint": endpoint,
            "status_code": status_code,
            "timestamp": firestore.SERVER_TIMESTAMP,
        }
        usage_ref.set(usage_data)

    @staticmethod
    def get_usage_by_email(email: str, last_days: int = 30) -> List[Dict[str, Any]]:
        """Get usage data for an email address."""
        from datetime import timedelta

        cutoff = datetime.now() - timedelta(days=last_days)
        usage = (
            db.collection("usage")
            .where("email", "==", email)
            .where("timestamp", ">=", cutoff)
            .order_by("timestamp", direction=firestore.Query.DESCENDING)
            .limit(1000)
            .stream()
        )
        result = []
        for doc in usage:
            data = doc.to_dict()
            if data:
                if "timestamp" in data and hasattr(data["timestamp"], "isoformat"):
                    data["timestamp"] = data["timestamp"].isoformat()
                result.append(data)
        return result

    @staticmethod
    def create_user(user_id: str, email: str, provider: str) -> Dict[str, Any]:
        """Create or update a user."""
        user_ref = db.collection("users").document(user_id)
        user_data = {
            "id": user_id,
            "email": email,
            "provider": provider,
            "created_at": firestore.SERVER_TIMESTAMP,
            "updated_at": firestore.SERVER_TIMESTAMP,
        }
        user_ref.set(user_data, merge=True)
        return user_data

    @staticmethod
    def get_user(user_id: str) -> Optional[Dict[str, Any]]:
        """Get a user by ID."""
        doc = db.collection("users").document(user_id).get()
        if doc.exists:
            return doc.to_dict()
        return None

    @staticmethod
    def get_1099_consent(email: str) -> Optional[Dict[str, Any]]:
        """Get 1099 electronic delivery consent status for an email."""
        doc = db.collection("1099_consents").document(email).get()
        if doc.exists:
            data = doc.to_dict()
            if data:
                if "consent_date" in data and hasattr(data["consent_date"], "isoformat"):
                    data["consent_date"] = data["consent_date"].isoformat()
                if "withdrawal_date" in data and hasattr(data["withdrawal_date"], "isoformat"):
                    data["withdrawal_date"] = data["withdrawal_date"].isoformat()
            return data
        return None

    @staticmethod
    def set_1099_consent(email: str, consented: bool, consent_date: datetime, method: str) -> None:
        """Store 1099 electronic delivery consent."""
        consent_ref = db.collection("1099_consents").document(email)
        consent_data = {
            "email": email,
            "consented": consented,
            "consent_date": consent_date,
            "method": method,  # e.g., "console", "email_link"
            "updated_at": firestore.SERVER_TIMESTAMP,
        }
        consent_ref.set(consent_data, merge=True)

    @staticmethod
    def withdraw_1099_consent(email: str) -> None:
        """Withdraw 1099 electronic delivery consent."""
        consent_ref = db.collection("1099_consents").document(email)
        consent_ref.update({
            "consented": False,
            "withdrawal_date": firestore.SERVER_TIMESTAMP,
            "updated_at": firestore.SERVER_TIMESTAMP,
        })

    @staticmethod
    def store_tax_info(email: str, tax_info: Dict[str, Any]) -> None:
        """Store tax information (SSN/EIN, address, name) for a developer."""
        tax_ref = db.collection("tax_information").document(email)
        tax_data = {
            "email": email,
            **tax_info,
            "updated_at": firestore.SERVER_TIMESTAMP,
        }
        tax_ref.set(tax_data, merge=True)

    @staticmethod
    def get_tax_info(email: str) -> Optional[Dict[str, Any]]:
        """Get tax information for a developer."""
        doc = db.collection("tax_information").document(email).get()
        if doc.exists:
            return doc.to_dict()
        return None

    @staticmethod
    def create_api_token(email: str, name: str) -> Dict[str, Any]:
        """Create a new API token for a developer.
        
        Args:
            email: Developer email address
            name: Human-readable name for the token
            
        Returns:
            Dictionary with token (plaintext, shown only once), id, name, created_at
        """
        # Generate secure random token (32 bytes = 256 bits, URL-safe base64)
        token = "agt_" + secrets.token_urlsafe(32)  # Prefix for AgenticlyPay tokens
        
        # Hash token with bcrypt (cost factor 12)
        token_hash = bcrypt.hashpw(token.encode('utf-8'), bcrypt.gensalt(rounds=12)).decode('utf-8')
        
        # Store in Firestore
        token_ref = db.collection("api_tokens").document()
        token_data = {
            "id": token_ref.id,
            "email": email,
            "name": name,
            "token_hash": token_hash,
            "created_at": firestore.SERVER_TIMESTAMP,
            "last_used_at": None,
            "is_active": True,
        }
        token_ref.set(token_data)
        
        # Return token info (including plaintext token - shown only once)
        return {
            "token": token,
            "id": token_ref.id,
            "name": name,
            "created_at": datetime.now().isoformat(),
        }

    @staticmethod
    def get_api_token_by_hash(token_hash: str) -> Optional[Dict[str, Any]]:
        """Get API token info by hashed token value.
        
        Args:
            token_hash: The hashed token to look up
            
        Returns:
            Dictionary with token info including email, or None if not found
        """
        # Note: This method is not used in the current implementation
        # We use verify_api_token instead which takes the plaintext token
        return None

    @staticmethod
    def verify_api_token(token: str) -> Optional[Dict[str, Any]]:
        """Verify an API token and return its info.
        
        Args:
            token: The plaintext token to verify
            
        Returns:
            Dictionary with token info including email, or None if invalid
        """
        # Query all active tokens and check hash
        tokens = db.collection("api_tokens").where("is_active", "==", True).stream()
        for doc in tokens:
            data = doc.to_dict()
            if data and data.get("token_hash"):
                # Verify the token matches the hash
                try:
                    if bcrypt.checkpw(token.encode('utf-8'), data["token_hash"].encode('utf-8')):
                        # Update last_used_at
                        doc.reference.update({"last_used_at": firestore.SERVER_TIMESTAMP})
                        return data
                except Exception:
                    continue
        return None

    @staticmethod
    def list_api_tokens(email: str) -> List[Dict[str, Any]]:
        """List all API tokens for a developer.
        
        Args:
            email: Developer email address
            
        Returns:
            List of token metadata (excludes token hashes and plaintext tokens)
        """
        tokens = (
            db.collection("api_tokens")
            .where("email", "==", email)
            .order_by("created_at", direction=firestore.Query.DESCENDING)
            .stream()
        )
        result = []
        for doc in tokens:
            data = doc.to_dict()
            if data:
                # Exclude sensitive data
                token_info = {
                    "id": data.get("id"),
                    "name": data.get("name"),
                    "created_at": data.get("created_at"),
                    "last_used_at": data.get("last_used_at"),
                    "is_active": data.get("is_active", True),
                }
                # Convert timestamps
                if "created_at" in token_info and hasattr(token_info["created_at"], "isoformat"):
                    token_info["created_at"] = token_info["created_at"].isoformat()
                if "last_used_at" in token_info and token_info["last_used_at"] and hasattr(token_info["last_used_at"], "isoformat"):
                    token_info["last_used_at"] = token_info["last_used_at"].isoformat()
                result.append(token_info)
        return result

    @staticmethod
    def revoke_api_token(email: str, token_id: str) -> None:
        """Revoke an API token.
        
        Args:
            email: Developer email address (for verification)
            token_id: Token ID to revoke
        """
        token_ref = db.collection("api_tokens").document(token_id)
        token_doc = token_ref.get()
        if token_doc.exists:
            token_data = token_doc.to_dict()
            # Verify the token belongs to this email
            if token_data and token_data.get("email") == email:
                token_ref.update({
                    "is_active": False,
                    "revoked_at": firestore.SERVER_TIMESTAMP,
                })

