"""
CRUD операції для Memory Service
"""

from typing import Optional, List, Dict, Any, Tuple
from sqlalchemy.orm import Session
from sqlalchemy import and_, or_, desc
from datetime import datetime

from app.models import UserFact, DialogSummary, AgentMemoryEvent, AgentMemoryFactsVector
from app.schemas import (
    UserFactCreate, UserFactUpdate, UserFactUpsertRequest,
    DialogSummaryCreate, AgentMemoryEventCreate
)


# ========== User Facts CRUD ==========

def get_user_fact(
    db: Session,
    user_id: str,
    fact_key: str,
    team_id: Optional[str] = None
) -> Optional[UserFact]:
    """Отримати факт користувача"""
    query = db.query(UserFact).filter(
        and_(
            UserFact.user_id == user_id,
            UserFact.fact_key == fact_key
        )
    )
    
    if team_id:
        query = query.filter(UserFact.team_id == team_id)
    else:
        query = query.filter(UserFact.team_id.is_(None))
    
    return query.first()


def get_user_facts(
    db: Session,
    user_id: str,
    team_id: Optional[str] = None,
    fact_keys: Optional[List[str]] = None,
    skip: int = 0,
    limit: int = 100
) -> List[UserFact]:
    """Отримати список фактів користувача"""
    query = db.query(UserFact).filter(UserFact.user_id == user_id)
    
    if team_id:
        query = query.filter(
            or_(
                UserFact.team_id == team_id,
                UserFact.team_id.is_(None)  # Глобальні факти
            )
        )
    
    if fact_keys:
        query = query.filter(UserFact.fact_key.in_(fact_keys))
    
    # Фільтр за терміном дії
    query = query.filter(
        or_(
            UserFact.expires_at.is_(None),
            UserFact.expires_at > datetime.utcnow()
        )
    )
    
    return query.offset(skip).limit(limit).all()


def create_user_fact(
    db: Session,
    fact: UserFactCreate
) -> UserFact:
    """Створити новий факт"""
    db_fact = UserFact(**fact.dict())
    db.add(db_fact)
    db.commit()
    db.refresh(db_fact)
    return db_fact


def update_user_fact(
    db: Session,
    fact_id: str,
    fact_update: UserFactUpdate
) -> Optional[UserFact]:
    """Оновити факт"""
    db_fact = db.query(UserFact).filter(UserFact.id == fact_id).first()
    if not db_fact:
        return None
    
    update_data = fact_update.dict(exclude_unset=True)
    for field, value in update_data.items():
        setattr(db_fact, field, value)
    
    db.commit()
    db.refresh(db_fact)
    return db_fact


def upsert_user_fact(
    db: Session,
    fact_request: UserFactUpsertRequest
) -> Tuple[UserFact, bool]:
    """
    Створити або оновити факт (upsert)
    Повертає (fact, created) де created = True якщо створено новий
    """
    # Шукаємо існуючий факт
    existing = get_user_fact(
        db,
        fact_request.user_id,
        fact_request.fact_key,
        fact_request.team_id
    )
    
    if existing:
        # Оновлюємо існуючий
        update_data = fact_request.dict(exclude={"user_id", "fact_key", "team_id"})
        for field, value in update_data.items():
            if value is not None:
                setattr(existing, field, value)
        db.commit()
        db.refresh(existing)
        return existing, False
    else:
        # Створюємо новий
        new_fact = UserFact(**fact_request.dict())
        db.add(new_fact)
        db.commit()
        db.refresh(new_fact)
        return new_fact, True


def delete_user_fact(
    db: Session,
    fact_id: str
) -> bool:
    """Видалити факт"""
    db_fact = db.query(UserFact).filter(UserFact.id == fact_id).first()
    if not db_fact:
        return False
    
    db.delete(db_fact)
    db.commit()
    return True


def get_user_facts_by_token_gate(
    db: Session,
    user_id: str,
    team_id: Optional[str] = None
) -> List[UserFact]:
    """Отримати токен-гейт факти користувача"""
    query = db.query(UserFact).filter(
        and_(
            UserFact.user_id == user_id,
            UserFact.token_gated == True
        )
    )
    
    if team_id:
        query = query.filter(
            or_(
                UserFact.team_id == team_id,
                UserFact.team_id.is_(None)
            )
        )
    
    return query.all()


# ========== Dialog Summary CRUD ==========

def create_dialog_summary(
    db: Session,
    summary: DialogSummaryCreate
) -> DialogSummary:
    """Створити підсумок діалогу"""
    db_summary = DialogSummary(**summary.dict())
    db.add(db_summary)
    db.commit()
    db.refresh(db_summary)
    return db_summary


def get_dialog_summaries(
    db: Session,
    team_id: Optional[str] = None,
    channel_id: Optional[str] = None,
    agent_id: Optional[str] = None,
    user_id: Optional[str] = None,
    skip: int = 0,
    limit: int = 50,
    cursor: Optional[str] = None
) -> Tuple[List[DialogSummary], Optional[str]]:
    """
    Отримати список підсумків діалогів з cursor pagination
    Повертає (summaries, next_cursor)
    """
    query = db.query(DialogSummary)
    
    if team_id:
        query = query.filter(DialogSummary.team_id == team_id)
    if channel_id:
        query = query.filter(DialogSummary.channel_id == channel_id)
    if agent_id:
        query = query.filter(DialogSummary.agent_id == agent_id)
    if user_id:
        query = query.filter(DialogSummary.user_id == user_id)
    
    if cursor:
        # Cursor-based pagination (використовуємо created_at)
        try:
            cursor_time = datetime.fromisoformat(cursor)
            query = query.filter(DialogSummary.created_at < cursor_time)
        except ValueError:
            pass
    
    query = query.order_by(desc(DialogSummary.created_at))
    
    results = query.offset(skip).limit(limit + 1).all()
    
    # Перевіряємо чи є наступна сторінка
    next_cursor = None
    if len(results) > limit:
        results = results[:limit]
        next_cursor = results[-1].created_at.isoformat()
    
    return results, next_cursor


def get_dialog_summary(
    db: Session,
    summary_id: str
) -> Optional[DialogSummary]:
    """Отримати підсумок за ID"""
    return db.query(DialogSummary).filter(DialogSummary.id == summary_id).first()


def delete_dialog_summary(
    db: Session,
    summary_id: str
) -> bool:
    """Видалити підсумок"""
    db_summary = db.query(DialogSummary).filter(DialogSummary.id == summary_id).first()
    if not db_summary:
        return False
    
    db.delete(db_summary)
    db.commit()
    return True


# ========== Agent Memory Event CRUD ==========

def create_agent_memory_event(
    db: Session,
    event: AgentMemoryEventCreate
) -> AgentMemoryEvent:
    """Створити подію пам'яті агента"""
    db_event = AgentMemoryEvent(**event.dict())
    db.add(db_event)
    db.commit()
    db.refresh(db_event)
    return db_event


def get_agent_memory_events(
    db: Session,
    agent_id: str,
    team_id: Optional[str] = None,
    channel_id: Optional[str] = None,
    scope: Optional[str] = None,
    kind: Optional[str] = None,
    skip: int = 0,
    limit: int = 50,
    cursor: Optional[str] = None
) -> Tuple[List[AgentMemoryEvent], Optional[str]]:
    """
    Отримати список подій пам'яті агента з cursor pagination
    """
    query = db.query(AgentMemoryEvent).filter(AgentMemoryEvent.agent_id == agent_id)
    
    if team_id:
        query = query.filter(AgentMemoryEvent.team_id == team_id)
    if channel_id:
        query = query.filter(AgentMemoryEvent.channel_id == channel_id)
    if scope:
        query = query.filter(AgentMemoryEvent.scope == scope)
    if kind:
        query = query.filter(AgentMemoryEvent.kind == kind)
    
    if cursor:
        try:
            cursor_time = datetime.fromisoformat(cursor)
            query = query.filter(AgentMemoryEvent.created_at < cursor_time)
        except ValueError:
            pass
    
    query = query.order_by(desc(AgentMemoryEvent.created_at))
    
    results = query.offset(skip).limit(limit + 1).all()
    
    next_cursor = None
    if len(results) > limit:
        results = results[:limit]
        next_cursor = results[-1].created_at.isoformat()
    
    return results, next_cursor


def delete_agent_memory_event(
    db: Session,
    event_id: str
) -> bool:
    """Видалити подію пам'яті"""
    db_event = db.query(AgentMemoryEvent).filter(AgentMemoryEvent.id == event_id).first()
    if not db_event:
        return False
    
    db.delete(db_event)
    db.commit()
    return True

