from fastapi import Depends, HTTPException, Request
from sqlalchemy.orm import Session

from flyway.auth.service import actor_for, load_session, renew_session
from flyway.config import settings
from flyway.db import get_session_factory
from flyway.domain.actor import Actor
from flyway.domain.clock import utcnow

SESSION_COOKIE = "flyway_session"
CSRF_HEADER = "X-CSRF-Token"
SAFE_METHODS = {"GET", "HEAD", "OPTIONS"}


def get_db():
    with get_session_factory()() as session:
        yield session


def set_session_cookie(response, token: str, persistent: bool) -> None:
    """“记住我”为持久 Cookie（有效期同会话），否则为浏览器会话 Cookie。"""
    response.set_cookie(SESSION_COOKIE, token, httponly=True, secure=settings.cookie_secure, samesite="lax",
                        max_age=settings.session_hours * 3600 if persistent else None)


def current_actor(request: Request, db: Session = Depends(get_db)) -> Actor:
    s = load_session(db, request.cookies.get(SESSION_COOKIE), utcnow())
    if s is None:
        raise HTTPException(status_code=401, detail="Not logged in.")
    if request.method not in SAFE_METHODS and request.headers.get(CSRF_HEADER) != s.csrf_token:
        raise HTTPException(status_code=403, detail="Invalid CSRF token.")
    request.state.csrf_token = s.csrf_token
    actor = actor_for(s.user)
    renewed = renew_session(db, s, utcnow())
    active = s.user.last_activity_at
    track_activity = active is None or (utcnow() - active).total_seconds() >= 60
    if track_activity:
        s.user.last_activity_at = utcnow()
    if renewed:
        request.state.renew_cookie = (request.cookies[SESSION_COOKIE], s.persistent)
    if renewed or track_activity:
        db.commit()
    return actor


def require_admin_actor(actor: Actor = Depends(current_actor)) -> Actor:
    if not actor.is_admin:
        raise HTTPException(status_code=403, detail="Permission denied.")
    return actor
