from rest_framework import status
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.response import Response
from rest_framework_simplejwt.tokens import RefreshToken

import secrets
import string

from .models import User, UserPrescription, ExerciseLog, RegistrationCode
from .serializers import (
    RegisterSerializer, LoginSerializer, UserSerializer,
    PatientSummarySerializer, PrescriptionSerializer, ExerciseLogSerializer,
    RegistrationCodeSerializer,
)


@api_view(['GET'])
@permission_classes([AllowAny])
def health(request):
    return Response({'status': 'ok'})


def _token_response(user):
    refresh = RefreshToken.for_user(user)
    return Response({
        'access': str(refresh.access_token),
        'refresh': str(refresh),
    }, status=status.HTTP_200_OK)


@api_view(['POST'])
@permission_classes([AllowAny])
def validate_code(request):
    """Check that an invite code is valid without consuming it."""
    code_str = request.data.get('code', '').strip().upper()
    try:
        code = RegistrationCode.objects.get(code=code_str)
    except RegistrationCode.DoesNotExist:
        return Response({'valid': False, 'detail': 'Invalid invite code.'}, status=status.HTTP_200_OK)
    if not code.is_valid():
        return Response({'valid': False, 'detail': 'This invite code has expired or already been used.'}, status=status.HTTP_200_OK)
    return Response({'valid': True}, status=status.HTTP_200_OK)


@api_view(['POST'])
@permission_classes([AllowAny])
def register(request):
    serializer = RegisterSerializer(data=request.data)
    if not serializer.is_valid():
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    user = serializer.save()
    return _token_response(user)


@api_view(['POST'])
@permission_classes([AllowAny])
def login(request):
    serializer = LoginSerializer(data=request.data)
    if not serializer.is_valid():
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    return _token_response(serializer.validated_data['user'])


@api_view(['POST'])
@permission_classes([IsAuthenticated])
def logout(request):
    try:
        refresh_token = request.data.get('refresh')
        if refresh_token:
            token = RefreshToken(refresh_token)
            token.blacklist()
    except Exception:
        pass
    return Response({'detail': 'Logged out.'}, status=status.HTTP_200_OK)


@api_view(['GET'])
@permission_classes([IsAuthenticated])
def me(request):
    serializer = UserSerializer(request.user)
    return Response(serializer.data)


# ---------------------------------------------------------------------------
# Therapist endpoints
# ---------------------------------------------------------------------------

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def patient_list(request):
    """Return all non-therapist users. Only accessible by therapists."""
    if not request.user.is_therapist:
        return Response({'detail': 'Forbidden.'}, status=status.HTTP_403_FORBIDDEN)
    patients = User.objects.filter(is_therapist=False, is_active=True).order_by('email')
    serializer = PatientSummarySerializer(patients, many=True)
    return Response(serializer.data)


@api_view(['PUT'])
@permission_classes([IsAuthenticated])
def prescribe(request, patient_id):
    """Create or update a prescription for a patient."""
    if not request.user.is_therapist:
        return Response({'detail': 'Forbidden.'}, status=status.HTTP_403_FORBIDDEN)
    try:
        patient = User.objects.get(pk=patient_id, is_therapist=False, is_active=True)
    except User.DoesNotExist:
        return Response({'detail': 'Patient not found.'}, status=status.HTTP_404_NOT_FOUND)

    prescription, _ = UserPrescription.objects.get_or_create(
        patient=patient,
        defaults={'therapist': request.user}
    )
    prescription.therapist = request.user
    serializer = PrescriptionSerializer(prescription, data=request.data, partial=True)
    if not serializer.is_valid():
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    serializer.save()
    return Response(serializer.data)


# ---------------------------------------------------------------------------
# Therapist: invite code management
# ---------------------------------------------------------------------------

@api_view(['GET', 'POST'])
@permission_classes([IsAuthenticated])
def codes(request):
    """GET: list this therapist's codes. POST: generate a new one."""
    if not request.user.is_therapist:
        return Response({'detail': 'Forbidden.'}, status=status.HTTP_403_FORBIDDEN)

    if request.method == 'GET':
        qs = RegistrationCode.objects.filter(therapist=request.user).order_by('-created_at')
        return Response(RegistrationCodeSerializer(qs, many=True).data)

    # POST — generate
    alphabet = string.ascii_uppercase + string.digits
    code_str = ''.join(secrets.choice(alphabet) for _ in range(8))
    code = RegistrationCode.objects.create(code=code_str, therapist=request.user)
    return Response(RegistrationCodeSerializer(code).data, status=status.HTTP_201_CREATED)


@api_view(['DELETE'])
@permission_classes([IsAuthenticated])
def revoke_code(request, code_id):
    """Delete (revoke) a pending code. Only the owning therapist can do this."""
    if not request.user.is_therapist:
        return Response({'detail': 'Forbidden.'}, status=status.HTTP_403_FORBIDDEN)
    try:
        code = RegistrationCode.objects.get(pk=code_id, therapist=request.user)
    except RegistrationCode.DoesNotExist:
        return Response({'detail': 'Not found.'}, status=status.HTTP_404_NOT_FOUND)
    if code.used_by_id is not None:
        return Response({'detail': 'Cannot revoke a code that has already been used.'}, status=status.HTTP_400_BAD_REQUEST)
    code.delete()
    return Response(status=status.HTTP_204_NO_CONTENT)


# ---------------------------------------------------------------------------
# Therapist: patient logs
# ---------------------------------------------------------------------------

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def patient_logs(request, patient_id):
    """Return exercise logs for a specific patient. Therapist-only."""
    if not request.user.is_therapist:
        return Response({'detail': 'Forbidden.'}, status=status.HTTP_403_FORBIDDEN)
    try:
        patient = User.objects.get(pk=patient_id, is_therapist=False, is_active=True)
    except User.DoesNotExist:
        return Response({'detail': 'Patient not found.'}, status=status.HTTP_404_NOT_FOUND)
    logs = patient.exercise_logs.all()[:50]  # most recent 50
    serializer = ExerciseLogSerializer(logs, many=True)
    return Response(serializer.data)


# ---------------------------------------------------------------------------
# Patient: log exercise + fetch own prescription
# ---------------------------------------------------------------------------

@api_view(['POST'])
@permission_classes([IsAuthenticated])
def log_exercise(request):
    """Patient submits an exercise log entry."""
    serializer = ExerciseLogSerializer(data=request.data)
    if not serializer.is_valid():
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    serializer.save(patient=request.user)
    return Response(serializer.data, status=status.HTTP_201_CREATED)


@api_view(['GET'])
@permission_classes([IsAuthenticated])
def my_logs(request):
    """Returns the current patient's own exercise logs (most recent 50)."""
    logs = request.user.exercise_logs.all()[:50]
    serializer = ExerciseLogSerializer(logs, many=True)
    return Response(serializer.data)


# ---------------------------------------------------------------------------
# Patient: fetch own prescription
# ---------------------------------------------------------------------------

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def my_prescription(request):
    """Returns the therapist-assigned prescription for the current user, if any."""
    try:
        p = request.user.prescription
        serializer = PrescriptionSerializer(p)
        return Response(serializer.data)
    except UserPrescription.DoesNotExist:
        return Response(None, status=status.HTTP_200_OK)
