import json
from typing import List, Optional

from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session

from .. import models, schemas
from ..db import get_db
from ..cache import CACHE_TTL_SECONDS, get_cache, safe_cache_get, safe_cache_set
from ..security import require_admin

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


def cache_rep(cache, rep: models.Reputation) -> None:
    safe_cache_set(
        cache,
        f"rep:{rep.entity_type}:{rep.entity_value}",
        json.dumps({"score": rep.score, "last_event": rep.last_event}),
        ex=CACHE_TTL_SECONDS,
    )


@router.get("/", response_model=List[schemas.ReputationOut])
def list_reputation(entity_type: Optional[schemas.ReputationEntityType] = Query(default=None), db: Session = Depends(get_db), _: str = Depends(require_admin)):
    query = db.query(models.Reputation)
    if entity_type:
        query = query.filter(models.Reputation.entity_type == entity_type)
    return query.all()


@router.get("/lookup", response_model=schemas.ReputationOut)
def lookup_reputation(entity_type: schemas.ReputationEntityType, entity_value: str, db: Session = Depends(get_db), cache=Depends(get_cache), _: str = Depends(require_admin)):
    cached = safe_cache_get(cache, f"rep:{entity_type}:{entity_value}")
    if cached:
        data = json.loads(cached)
        return schemas.ReputationOut(id=0, entity_type=entity_type, entity_value=entity_value, score=data.get("score", 0), last_event=data.get("last_event"), updated_at=models.utc_now())
    rep = db.query(models.Reputation).filter(models.Reputation.entity_type == entity_type, models.Reputation.entity_value == entity_value).first()
    if not rep:
        raise HTTPException(status_code=404, detail="Not found")
    cache_rep(cache, rep)
    return rep


@router.post("/", response_model=schemas.ReputationOut)
def create_reputation(payload: schemas.ReputationCreate, db: Session = Depends(get_db), cache=Depends(get_cache), _: str = Depends(require_admin)):
    rep = db.query(models.Reputation).filter(
        models.Reputation.entity_type == payload.entity_type,
        models.Reputation.entity_value == payload.entity_value,
    ).first()
    if rep:
        rep.score = payload.score
        rep.last_event = payload.last_event
    else:
        rep = models.Reputation(
            entity_type=payload.entity_type,
            entity_value=payload.entity_value,
            score=payload.score,
            last_event=payload.last_event,
        )
        db.add(rep)
    try:
        db.commit()
    except IntegrityError as exc:
        db.rollback()
        raise HTTPException(status_code=409, detail="Reputation entry was modified concurrently") from exc
    db.refresh(rep)
    cache_rep(cache, rep)
    return rep


@router.patch("/{rep_id}", response_model=schemas.ReputationOut)
def update_reputation(rep_id: int, payload: schemas.ReputationUpdate, db: Session = Depends(get_db), cache=Depends(get_cache), _: str = Depends(require_admin)):
    rep = db.query(models.Reputation).filter(models.Reputation.id == rep_id).first()
    if not rep:
        raise HTTPException(status_code=404, detail="Not found")
    if payload.score is not None:
        rep.score = payload.score
    if payload.last_event is not None:
        rep.last_event = payload.last_event
    db.commit()
    db.refresh(rep)
    cache_rep(cache, rep)
    return rep
