from sqlalchemy.orm import Session
from fastapi import HTTPException, status
from typing import Optional, List
from datetime import datetime
from . import repository
from .schema import CalculationsessionCreate, CalculationsessionUpdate, CalculationsessionResponse


def _get_or_raise(db: Session, entity_id: str) -> repository.Calculationsession:
    entity = repository.get_by_id(db, entity_id)
    if not entity:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Calculation session with id {entity_id} not found"
        )
    return entity


def create_calculation_session(db: Session, data: CalculationsessionCreate) -> CalculationsessionResponse:
    existing = repository.get_by_session_id(db, data.session_id)
    if existing:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail=f"Calculation session with session_id {data.session_id} already exists"
        )
    
    try:
        session = repository.create(db, data.model_dump())
        db.commit()
        db.refresh(session)
        return CalculationsessionResponse.model_validate(session)
    except Exception:
        db.rollback()
        raise


def get_calculation_session(db: Session, entity_id: str) -> CalculationsessionResponse:
    session = _get_or_raise(db, entity_id)
    return CalculationsessionResponse.model_validate(session)


def get_calculation_session_by_session_id(db: Session, session_id: str) -> CalculationsessionResponse:
    session = repository.get_by_session_id(db, session_id)
    if not session:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Calculation session with session_id {session_id} not found"
        )
    return CalculationsessionResponse.model_validate(session)


def list_calculation_sessions(
    db: Session,
    limit: int = 20,
    offset: int = 0,
    session_id: Optional[str] = None
) -> List[CalculationsessionResponse]:
    filters = {}
    if session_id:
        filters["session_id"] = session_id
    
    sessions = repository.list_all(db, limit, offset, **filters)
    return [CalculationsessionResponse.model_validate(s) for s in sessions]


def update_calculation_session(
    db: Session,
    entity_id: str,
    data: CalculationsessionUpdate
) -> CalculationsessionResponse:
    _get_or_raise(db, entity_id)
    
    update_data = data.model_dump(exclude_unset=True)
    
    if "session_id" in update_data:
        existing = repository.get_by_session_id(db, update_data["session_id"])
        if existing and existing.id != entity_id:
            raise HTTPException(
                status_code=status.HTTP_409_CONFLICT,
                detail=f"Calculation session with session_id {update_data['session_id']} already exists"
            )
    
    try:
        session = repository.update(db, entity_id, update_data)
        db.commit()
        db.refresh(session)
        return CalculationsessionResponse.model_validate(session)
    except Exception:
        db.rollback()
        raise


def delete_calculation_session(db: Session, entity_id: str) -> None:
    _get_or_raise(db, entity_id)
    
    try:
        repository.delete(db, entity_id)
        db.commit()
    except Exception:
        db.rollback()
        raise


def increment_calculation_count(db: Session, session_id: str) -> CalculationsessionResponse:
    session = repository.get_by_session_id(db, session_id)
    if not session:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Calculation session with session_id {session_id} not found"
        )
    
    try:
        update_data = {
            "calculation_count": session.calculation_count + 1,
            "last_activity_at": datetime.utcnow()
        }
        session = repository.update(db, session.id, update_data)
        db.commit()
        db.refresh(session)
        return CalculationsessionResponse.model_validate(session)
    except Exception:
        db.rollback()
        raise