from pydantic import BaseModel, Field  # type: ignore
from datetime import datetime
from typing import Optional, List
from enum import Enum
from bson import ObjectId

# ========== Helper for ObjectId handling ==========


class PyObjectId(ObjectId):
    """Custom type for ObjectId serialization"""
    @classmethod
    def __get_validators__(cls):
        yield cls.validate

    @classmethod
    def validate(cls, v):
        if isinstance(v, ObjectId):
            return v
        if isinstance(v, str):
            return ObjectId(v)
        raise ValueError(f"Invalid ObjectId: {v}")

    def __repr__(self):
        return f"ObjectId('{self}')"


# ========== User Models ==========


class UserCreate(BaseModel):
    email: str
    displayName: Optional[str] = None
    photoURL: Optional[str] = None


class UserUpdate(BaseModel):
    displayName: Optional[str] = None
    photoURL: Optional[str] = None
    theme: Optional[str] = None
    tutorial: Optional[bool] = None
    backgroundImage: Optional[str] = None
    backgroundImages: Optional[List[str]] = None

    class Config:
        json_schema_extra = {
            "example": {
                "displayName": "John Doe",
                "theme": "dark"
            }
        }


class User(BaseModel):
    id: str = Field(alias="_id")
    email: str
    displayName: Optional[str] = None
    photoURL: Optional[str] = None
    createdAt: datetime
    updatedAt: datetime
    theme: str = "light"
    tutorial: Optional[bool] = None

    class Config:
        from_attributes = True
        populate_by_name = True
        json_schema_extra = {
            "example": {
                "_id": "507f1f77bcf86cd799439011",
                "email": "user@example.com",
                "displayName": "John Doe",
                "photoURL": "https://example.com/photo.jpg",
                "createdAt": "2026-03-05T00:00:00Z",
                "updatedAt": "2026-03-05T00:00:00Z",
                "theme": "light"
            }
        }


# ========== Journal Entry Models ==========


class MoodEnum(str, Enum):
    happy = "happy"
    sad = "sad"
    neutral = "neutral"
    anxious = "anxious"
    grateful = "grateful"


class EncryptionMetadata(BaseModel):
    """Encryption metadata for entries - zero-knowledge privacy"""
    encrypted: bool = True
    ciphertext: str  # Base64-encoded encrypted content
    nonce: str  # Base64-encoded nonce used for encryption
    algorithm: str = "XSalsa20-Poly1305"  # crypto_secretbox algorithm

    class Config:
        json_schema_extra = {
            "example": {
                "encrypted": True,
                "ciphertext": "base64_encoded_ciphertext...",
                "nonce": "base64_encoded_nonce...",
                "algorithm": "XSalsa20-Poly1305"
            }
        }


class JournalEntryCreate(BaseModel):
    title: Optional[str] = None  # Optional if encrypted
    content: Optional[str] = None  # Optional if encrypted
    mood: Optional[MoodEnum] = None
    tags: Optional[List[str]] = None
    isPublic: Optional[bool] = False
    # Logical journal date; defaults to today
    entryDate: Optional[datetime] = None
    # Encryption metadata - present if entry is encrypted
    encryption: Optional[EncryptionMetadata] = None

    class Config:
        json_schema_extra = {
            "example": {
                "encryption": {
                    "encrypted": True,
                    "ciphertext": "base64_ciphertext...",
                    "nonce": "base64_nonce...",
                    "algorithm": "XSalsa20-Poly1305"
                },
                "mood": "grateful",
                "tags": ["work", "family"],
                "isPublic": False,
                "entryDate": "2026-03-05T00:00:00Z"
            }
        }


class JournalEntryUpdate(BaseModel):
    title: Optional[str] = None
    content: Optional[str] = None
    mood: Optional[MoodEnum] = None
    tags: Optional[List[str]] = None
    isPublic: Optional[bool] = None
    encryption: Optional[EncryptionMetadata] = None

    class Config:
        json_schema_extra = {
            "example": {
                "title": "Updated Title",
                "mood": "happy"
            }
        }


class JournalEntry(BaseModel):
    id: str = Field(alias="_id")
    userId: str  # ObjectId as string
    title: Optional[str] = None  # None if encrypted
    content: Optional[str] = None  # None if encrypted
    mood: Optional[MoodEnum] = None
    tags: Optional[List[str]] = []
    isPublic: bool = False
    entryDate: datetime  # Logical journal date
    createdAt: datetime
    updatedAt: datetime
    encryption: Optional[EncryptionMetadata] = None  # Present if encrypted

    class Config:
        from_attributes = True
        populate_by_name = True
        json_schema_extra = {
            "example": {
                "_id": "507f1f77bcf86cd799439011",
                "userId": "507f1f77bcf86cd799439012",
                "encryption": {
                    "encrypted": True,
                    "ciphertext": "base64_ciphertext...",
                    "nonce": "base64_nonce...",
                    "algorithm": "XSalsa20-Poly1305"
                },
                "mood": "grateful",
                "tags": ["work", "family"],
                "isPublic": False,
                "entryDate": "2026-03-05T00:00:00Z",
                "createdAt": "2026-03-05T12:00:00Z",
                "updatedAt": "2026-03-05T12:00:00Z"
            }
        }


# ========== Pagination Models ==========


class PaginationMeta(BaseModel):
    """Pagination metadata for list responses"""
    total: int
    limit: int
    skip: int
    hasMore: bool

    class Config:
        json_schema_extra = {
            "example": {
                "total": 42,
                "limit": 20,
                "skip": 0,
                "hasMore": True
            }
        }


class EntriesListResponse(BaseModel):
    """Response model for paginated entries"""
    entries: List[JournalEntry]
    pagination: PaginationMeta

    class Config:
        json_schema_extra = {
            "example": {
                "entries": [],
                "pagination": {
                    "total": 42,
                    "limit": 20,
                    "skip": 0,
                    "hasMore": True
                }
            }
        }
