import ipaddress
from datetime import timedelta
from django.conf import settings
from django.db import transaction
from django.utils import timezone
from rest_framework.exceptions import AuthenticationFailed
from rest_framework_simplejwt.tokens import RefreshToken
from .models import User
from .security_models import AccountSession


def assert_session(token, user):
    sid = token.get('session_id')
    if sid and not AccountSession.objects.filter(pk=sid,user=user,revoked_at__isnull=True,expires_at__gt=timezone.now()).exists():
        raise AuthenticationFailed('This device session was revoked or expired.')


@transaction.atomic
def issue(user, request=None, method='PASSWORD'):
    fresh = User.objects.select_for_update().get(pk=user.pk)
    if not fresh.is_active or not fresh.is_email_verified or fresh.auth_version != user.auth_version:
        raise AuthenticationFailed('Account changed. Sign in again.')
    ip = request.META.get('REMOTE_ADDR') if request else None
    try:
        ip = str(ipaddress.ip_address(ip)) if ip else None
    except ValueError:
        ip = None
    session = AccountSession.objects.create(user=fresh,method=method,
        user_agent=(request.headers.get('User-Agent','') if request else '')[:350],ip_address=ip,
        expires_at=timezone.now()+settings.SIMPLE_JWT['REFRESH_TOKEN_LIFETIME'])
    refresh=RefreshToken.for_user(fresh)
    refresh['auth_version']=str(fresh.auth_version)
    refresh['session_id']=str(session.pk)
    fresh.last_login=timezone.now();fresh.save(update_fields=['last_login'])
    return {'refresh':str(refresh),'access':str(refresh.access_token)}


def touch(token):
    sid=token.get('session_id')
    if sid:
        AccountSession.objects.filter(pk=sid,last_seen_at__lt=timezone.now()-timedelta(minutes=5)).update(last_seen_at=timezone.now())


def revoke_all(user):
    AccountSession.objects.filter(user=user,revoked_at__isnull=True).update(revoked_at=timezone.now())
