"""Entity endpoints - /api/v1/entities."""

from typing import Annotated, Any

from fastapi import APIRouter, Depends, HTTPException, Query, Request, status
from pydantic import BaseModel

from remembra.auth.middleware import CurrentUser
from remembra.core.limiter import limiter
from remembra.storage.database import Database

router = APIRouter(prefix="/entities", tags=["entities"])


def get_database(request: Request) -> Database:
    """Dependency to get the database from app state."""
    return request.app.state.db


DatabaseDep = Annotated[Database, Depends(get_database)]


# ---------------------------------------------------------------------------
# Models
# ---------------------------------------------------------------------------


class EntityResponse(BaseModel):
    """Single entity response."""
    id: str
    canonical_name: str
    type: str
    aliases: list[str] = []
    attributes: dict[str, Any] = {}
    confidence: float = 1.0
    memory_count: int = 0  # Number of memories linked to this entity


class RelationshipResponse(BaseModel):
    """Relationship between entities with temporal validity."""
    id: str
    from_entity_id: str
    from_entity_name: str
    to_entity_id: str
    to_entity_name: str
    type: str
    confidence: float = 1.0
    # Temporal validity (bi-temporal edges)
    valid_from: str | None = None  # When relationship became true
    valid_to: str | None = None    # When relationship ended (None = still valid)
    is_current: bool = True        # Whether relationship is currently valid
    superseded_by: str | None = None  # ID of relationship that supersedes this


class EntitiesListResponse(BaseModel):
    """Response for listing entities."""
    entities: list[EntityResponse]
    total: int
    by_type: dict[str, int]  # Count by entity type


class RelationshipsListResponse(BaseModel):
    """Response for listing relationships."""
    relationships: list[RelationshipResponse]
    total: int


# ---------------------------------------------------------------------------
# Endpoints
# ---------------------------------------------------------------------------


@router.get(
    "",
    response_model=EntitiesListResponse,
    summary="List all entities",
)
@limiter.limit("60/minute")
async def list_entities(
    request: Request,
    db: DatabaseDep,
    current_user: CurrentUser,
    project_id: str | None = Query(default=None, description="Filter by project (omit to see all projects)"),
    entity_type: str | None = Query(default=None, description="Filter by type (person, company, location, concept)"),
    limit: int = Query(default=100, ge=1, le=500),
) -> EntitiesListResponse:
    """
    List all entities for the authenticated user.
    
    Entities are automatically extracted from stored memories and include:
    - People (person)
    - Organizations/Companies (company, organization)
    - Locations/Places (location, place)
    - Concepts (concept)
    """
    # Get entities from database
    if entity_type:
        entities = await db.get_entities_by_type(
            user_id=current_user.user_id,
            project_id=project_id,
            entity_type=entity_type,
        )
    else:
        entities = await db.get_user_entities(
            user_id=current_user.user_id,
            project_id=project_id,
        )
    
    # Limit results
    entities = entities[:limit]
    
    # Get memory counts for each entity
    entity_responses = []
    type_counts: dict[str, int] = {}
    
    for entity in entities:
        # Count linked memories
        memory_ids = await db.get_memories_by_entity(entity.id)
        
        entity_responses.append(EntityResponse(
            id=entity.id,
            canonical_name=entity.canonical_name,
            type=entity.type,
            aliases=entity.aliases,
            attributes=entity.attributes,
            confidence=entity.confidence,
            memory_count=len(memory_ids),
        ))
        
        # Track type counts
        t = entity.type.lower()
        type_counts[t] = type_counts.get(t, 0) + 1
    
    return EntitiesListResponse(
        entities=entity_responses,
        total=len(entity_responses),
        by_type=type_counts,
    )


@router.get(
    "/{entity_id}",
    response_model=EntityResponse,
    summary="Get entity by ID",
)
@limiter.limit("60/minute")
async def get_entity(
    request: Request,
    entity_id: str,
    db: DatabaseDep,
    current_user: CurrentUser,
) -> EntityResponse:
    """Get a specific entity by ID."""
    entity = await db.get_entity(entity_id)
    
    if not entity:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Entity {entity_id} not found",
        )
    
    # Get linked memories
    memory_ids = await db.get_memories_by_entity(entity_id)
    
    return EntityResponse(
        id=entity.id,
        canonical_name=entity.canonical_name,
        type=entity.type,
        aliases=entity.aliases,
        attributes=entity.attributes,
        confidence=entity.confidence,
        memory_count=len(memory_ids),
    )


@router.get(
    "/{entity_id}/relationships",
    response_model=RelationshipsListResponse,
    summary="Get relationships for an entity",
)
@limiter.limit("60/minute")
async def get_entity_relationships(
    request: Request,
    entity_id: str,
    db: DatabaseDep,
    current_user: CurrentUser,
    as_of: str | None = Query(
        default=None,
        description="Point-in-time query (ISO format, e.g., '2022-01-15'). Returns relationships valid at this time.",
    ),
    include_history: bool = Query(
        default=False,
        description="Include superseded/historical relationships",
    ),
) -> RelationshipsListResponse:
    """
    Get relationships involving a specific entity with temporal filtering.
    
    Supports point-in-time queries like "Where did Alice work in January 2022?"
    
    - **as_of**: Query relationships as they were at a specific date
    - **include_history**: Include relationships that have been superseded
    """
    from datetime import datetime
    
    # Verify entity exists
    entity = await db.get_entity(entity_id)
    if not entity:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Entity {entity_id} not found",
        )
    
    # Parse as_of date if provided
    as_of_dt = None
    if as_of:
        try:
            as_of_dt = datetime.fromisoformat(as_of.replace("Z", "+00:00"))
        except ValueError:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=f"Invalid date format for as_of: {as_of}. Use ISO format (e.g., 2022-01-15)",
            )
    
    # Get relationships with temporal filtering
    relationships = await db.get_entity_relationships(
        entity_id,
        as_of=as_of_dt,
        include_superseded=include_history,
    )
    
    # Enrich with entity names
    relationship_responses = []
    for rel in relationships:
        from_entity = await db.get_entity(rel.from_entity_id)
        to_entity = await db.get_entity(rel.to_entity_id)
        
        relationship_responses.append(RelationshipResponse(
            id=rel.id,
            from_entity_id=rel.from_entity_id,
            from_entity_name=from_entity.canonical_name if from_entity else "Unknown",
            to_entity_id=rel.to_entity_id,
            to_entity_name=to_entity.canonical_name if to_entity else "Unknown",
            type=rel.type,
            confidence=rel.confidence,
            valid_from=rel.valid_from.isoformat() if rel.valid_from else None,
            valid_to=rel.valid_to.isoformat() if rel.valid_to else None,
            is_current=rel.is_current,
            superseded_by=rel.superseded_by,
        ))
    
    return RelationshipsListResponse(
        relationships=relationship_responses,
        total=len(relationship_responses),
    )


@router.get(
    "/relationships",
    response_model=RelationshipsListResponse,
    summary="Search relationships by entity name",
)
@limiter.limit("60/minute")
async def search_relationships_by_name(
    request: Request,
    db: DatabaseDep,
    current_user: CurrentUser,
    entity_name: str = Query(..., description="Entity name to search for"),
    relationship_type: str | None = Query(default=None, description="Filter by type (WORKS_AT, SPOUSE_OF, etc)"),
    as_of: str | None = Query(default=None, description="Point-in-time query (ISO format)"),
    include_history: bool = Query(default=False, description="Include superseded relationships"),
    project_id: str = Query(default="default"),
) -> RelationshipsListResponse:
    """
    Search relationships by entity name with temporal filtering.
    
    This is the main endpoint for temporal relationship queries like:
    - "Where did Alice work in January 2022?"
    - "Who was Bob married to in 2019?"
    
    The bi-temporal model tracks both when we learned about a relationship
    (created_at) and when it was actually true (valid_from/valid_to).
    """
    from datetime import datetime
    
    # Parse as_of date if provided
    as_of_dt = None
    if as_of:
        try:
            as_of_dt = datetime.fromisoformat(as_of.replace("Z", "+00:00"))
        except ValueError:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=f"Invalid date format for as_of: {as_of}. Use ISO format (e.g., 2022-01-15)",
            )
    
    # Find entity by name
    entity = await db.find_entity_by_name(
        name=entity_name,
        user_id=current_user.user_id,
        project_id=project_id,
    )
    
    if not entity:
        # Return empty list if entity not found (don't error)
        return RelationshipsListResponse(relationships=[], total=0)
    
    # Get relationships with temporal filtering
    relationships = await db.get_entity_relationships(
        entity.id,
        as_of=as_of_dt,
        include_superseded=include_history,
    )
    
    # Filter by relationship type if specified
    if relationship_type:
        relationships = [r for r in relationships if r.type.upper() == relationship_type.upper()]
    
    # Enrich with entity names
    relationship_responses = []
    for rel in relationships:
        from_entity = await db.get_entity(rel.from_entity_id)
        to_entity = await db.get_entity(rel.to_entity_id)
        
        relationship_responses.append(RelationshipResponse(
            id=rel.id,
            from_entity_id=rel.from_entity_id,
            from_entity_name=from_entity.canonical_name if from_entity else "Unknown",
            to_entity_id=rel.to_entity_id,
            to_entity_name=to_entity.canonical_name if to_entity else "Unknown",
            type=rel.type,
            confidence=rel.confidence,
            valid_from=rel.valid_from.isoformat() if rel.valid_from else None,
            valid_to=rel.valid_to.isoformat() if rel.valid_to else None,
            is_current=rel.is_current,
            superseded_by=rel.superseded_by,
        ))
    
    return RelationshipsListResponse(
        relationships=relationship_responses,
        total=len(relationship_responses),
    )


@router.get(
    "/{entity_id}/memories",
    summary="Get memories linked to an entity",
)
@limiter.limit("60/minute")
async def get_entity_memories(
    request: Request,
    entity_id: str,
    db: DatabaseDep,
    current_user: CurrentUser,
    limit: int = Query(default=20, ge=1, le=100),
) -> dict[str, Any]:
    """Get all memories that mention or are linked to a specific entity."""
    # Verify entity exists
    entity = await db.get_entity(entity_id)
    if not entity:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Entity {entity_id} not found",
        )
    
    # Get linked memory IDs
    memory_ids = await db.get_memories_by_entity(entity_id)
    
    # Get memory details
    memories = []
    for mid in memory_ids[:limit]:
        memory = await db.get_memory(mid)
        if memory:
            memories.append({
                "id": memory["id"],
                "content": memory["content"],
                "created_at": memory["created_at"],
            })
    
    return {
        "entity_id": entity_id,
        "entity_name": entity.canonical_name,
        "memories": memories,
        "total": len(memory_ids),
    }
