import random
from django.utils import timezone
from datetime import timedelta
from django.core.mail import send_mail
from django.conf import settings
from rest_framework import generics, permissions
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.parsers import MultiPartParser, FormParser, JSONParser
from rest_framework_simplejwt.tokens import RefreshToken
from .models import User, PasswordResetToken, EmailVerification
from .serializers import RegisterSerializer, LoginSerializer, UserSerializer


def get_tokens_for_user(user):
    refresh = RefreshToken.for_user(user)
    return {
        "refresh": str(refresh),
        "access":  str(refresh.access_token),
    }


def generate_otp():
    return str(random.randint(100000, 999999))


def send_otp_email(user, otp):
    try:
        send_mail(
            subject="ThikaConnect — Verify Your Email",
            message=(
                f"Hi {user.username},\n\n"
                f"Your ThikaConnect verification code is:\n\n"
                f"  {otp}\n\n"
                f"This code expires in 15 minutes.\n\n"
                f"If you did not create an account, ignore this email.\n\n"
                f"— ThikaConnect Team"
            ),
            from_email=settings.DEFAULT_FROM_EMAIL,
            recipient_list=[user.email],
            fail_silently=False,
        )
        return True
    except Exception as e:
        print(f"Email send error: {e}")
        return False


class RegisterView(generics.CreateAPIView):
    serializer_class   = RegisterSerializer
    permission_classes = [permissions.AllowAny]

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = serializer.save()

        # Set inactive until verified
        user.is_active = False
        user.save(update_fields=["is_active"])

        # Generate OTP
        otp = generate_otp()
        expires_at = timezone.now() + timedelta(minutes=15)

        # Delete any existing OTP for this user
        EmailVerification.objects.filter(user=user).delete()
        EmailVerification.objects.create(
            user=user, otp=otp, expires_at=expires_at
        )

        # Send email
        email_sent = send_otp_email(user, otp)

        return Response({
            "message": "Account created. Check your email for the verification code.",
            "email": user.email,
            "email_sent": email_sent,
        }, status=201)

    def perform_create(self, serializer):
        return serializer.save()


class VerifyEmailView(APIView):
    permission_classes = [permissions.AllowAny]

    def post(self, request):
        email = request.data.get("email", "").strip().lower()
        otp   = request.data.get("otp", "").strip()

        if not email or not otp:
            return Response({"error": "Email and OTP are required."}, status=400)

        try:
            user = User.objects.get(email=email)
        except User.DoesNotExist:
            return Response({"error": "User not found."}, status=404)

        if user.is_active:
            # Already verified — just log them in
            tokens = get_tokens_for_user(user)
            return Response({
                "message": "Already verified.",
                "tokens": tokens,
                "user": UserSerializer(user).data,
            })

        try:
            verification = EmailVerification.objects.get(user=user, is_used=False)
        except EmailVerification.DoesNotExist:
            return Response({"error": "No pending verification. Request a new code."}, status=400)

        if verification.is_expired():
            return Response({"error": "Code expired. Request a new one."}, status=400)

        if verification.otp != otp:
            return Response({"error": "Invalid code. Please check and try again."}, status=400)

        # Activate user
        user.is_active = True
        user.save(update_fields=["is_active"])
        verification.is_used = True
        verification.save(update_fields=["is_used"])

        tokens = get_tokens_for_user(user)
        return Response({
            "message": "Email verified! Welcome to ThikaConnect.",
            "tokens": tokens,
            "user": UserSerializer(user).data,
        })


class ResendOTPView(APIView):
    permission_classes = [permissions.AllowAny]

    def post(self, request):
        email = request.data.get("email", "").strip().lower()
        if not email:
            return Response({"error": "Email is required."}, status=400)

        try:
            user = User.objects.get(email=email)
        except User.DoesNotExist:
            return Response({"error": "User not found."}, status=404)

        if user.is_active:
            return Response({"error": "Account already verified."}, status=400)

        otp        = generate_otp()
        expires_at = timezone.now() + timedelta(minutes=15)

        EmailVerification.objects.filter(user=user).delete()
        EmailVerification.objects.create(user=user, otp=otp, expires_at=expires_at)

        email_sent = send_otp_email(user, otp)

        if not email_sent:
            return Response({"error": "Failed to send email. Try again."}, status=500)

        return Response({"message": "New verification code sent."})


class LoginView(APIView):
    permission_classes = [permissions.AllowAny]

    def post(self, request):
        serializer = LoginSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = serializer.validated_data["user"]

        if not user.is_active:
            # Resend OTP automatically
            otp        = generate_otp()
            expires_at = timezone.now() + timedelta(minutes=15)
            EmailVerification.objects.filter(user=user).delete()
            EmailVerification.objects.create(user=user, otp=otp, expires_at=expires_at)
            send_otp_email(user, otp)
            return Response({
                "error": "account_not_verified",
                "email": user.email,
                "message": "Please verify your email. A new code has been sent.",
            }, status=403)

        tokens = get_tokens_for_user(user)
        return Response({
            "tokens": tokens,
            "user":   UserSerializer(user).data,
        })


class ProfileView(generics.RetrieveAPIView):
    serializer_class   = UserSerializer
    permission_classes = [permissions.IsAuthenticated]

    def get_object(self):
        return self.request.user


class UpdateProfileView(APIView):
    permission_classes = [permissions.IsAuthenticated]
    parser_classes     = [MultiPartParser, FormParser, JSONParser]

    def put(self, request, *args, **kwargs):
        return self._update(request)

    def patch(self, request, *args, **kwargs):
        return self._update(request)

    def _update(self, request):
        user = request.user
        for field in ["username", "bio", "phone", "neighborhood", "ward"]:
            val = request.data.get(field)
            if val is not None:
                setattr(user, field, val)
        if "profile_image" in request.FILES:
            user.profile_image = request.FILES["profile_image"]
        user.save()

        # Award one-time profile completion bonus
        if user.bio and user.phone and user.profile_image and user.neighborhood:
            try:
                from wallet.models import Transaction
                from wallet.utils import award_points, get_or_create_wallet
                wallet = get_or_create_wallet(user)
                already = Transaction.objects.filter(wallet=wallet, tx_type="profile_complete").exists()
                if not already:
                    award_points(user, 50, "profile_complete", "Completed your profile")
            except Exception:
                pass

        return Response(UserSerializer(user).data)


class ChangePasswordView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request):
        old_password = request.data.get("old_password", "")
        new_password = request.data.get("new_password", "")
        if not request.user.check_password(old_password):
            return Response({"error": "Old password is incorrect."}, status=400)
        if len(new_password) < 8:
            return Response({"error": "New password must be at least 8 characters."}, status=400)
        request.user.set_password(new_password)
        request.user.save()
        return Response({"message": "Password changed successfully."})


class ForgotPasswordView(APIView):
    permission_classes = [permissions.AllowAny]

    def post(self, request):
        email = request.data.get("email", "").strip().lower()
        try:
            user = User.objects.get(email=email)
        except User.DoesNotExist:
            return Response({"message": "If that email exists, a reset link has been sent."})

        import secrets
        token = secrets.token_urlsafe(32)
        PasswordResetToken.objects.filter(user=user).delete()
        PasswordResetToken.objects.create(user=user, token=token)

        reset_url = f"http://localhost:5174/reset-password?token={token}"
        try:
            send_mail(
                subject="ThikaConnect — Reset Your Password",
                message=f"Hi {user.username},\n\nReset your password:\n{reset_url}\n\nThis link expires in 1 hour.\n\n— ThikaConnect",
                from_email=settings.DEFAULT_FROM_EMAIL,
                recipient_list=[user.email],
                fail_silently=True,
            )
        except Exception:
            pass

        return Response({"message": "If that email exists, a reset link has been sent."})


class ResetPasswordView(APIView):
    permission_classes = [permissions.AllowAny]

    def post(self, request):
        token    = request.data.get("token", "")
        password = request.data.get("password", "")

        try:
            reset = PasswordResetToken.objects.get(token=token)
        except PasswordResetToken.DoesNotExist:
            return Response({"error": "Invalid or expired reset link."}, status=400)

        if len(password) < 8:
            return Response({"error": "Password must be at least 8 characters."}, status=400)

        reset.user.set_password(password)
        reset.user.save()
        reset.delete()
        return Response({"message": "Password reset successfully."})


class PublicProfileView(generics.RetrieveAPIView):
    serializer_class   = UserSerializer
    permission_classes = [permissions.IsAuthenticated]
    queryset           = User.objects.all()
    lookup_field       = "pk"

    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        data = serializer.data
        # Add follow status
        from followers.models import Follow
        data["is_following"] = Follow.objects.filter(follower=request.user, following=instance).exists()
        data["is_me"] = request.user.id == instance.id
        return Response(data)


class VerificationApplyView(APIView):
    """User spends 500 points to apply for verification."""
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request):
        user = request.user
        if user.is_verified:
            return Response({"error": "Already verified."}, status=400)
        if user.verification_status == "pending":
            return Response({"error": "Application already pending."}, status=400)

        # Deduct 500 points
        try:
            from wallet.utils import get_or_create_wallet, award_points
            from wallet.models import Transaction
            wallet = get_or_create_wallet(user)
            if wallet.balance < 500:
                return Response({"error": f"You need 500 points. Your balance: {wallet.balance}."}, status=400)
            wallet.balance -= 500
            wallet.save(update_fields=["balance"])
            Transaction.objects.create(wallet=wallet, amount=-500, tx_type="admin_adjust", description="Verification application fee")
        except Exception as e:
            return Response({"error": str(e)}, status=500)

        user.verification_status = "pending"
        user.save(update_fields=["verification_status"])

        # Notify admins
        try:
            from notifications.utils import create_notification
            for admin in User.objects.filter(is_staff=True):
                create_notification(admin, "system", f"{user.username} applied for verification ✅", link="/admin/")
        except Exception:
            pass

        return Response({"status": "pending", "message": "Application submitted! Admins will review within 24h."})


class VerificationApproveView(APIView):
    """Staff-only: approve or reject a verification application."""
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request, user_id):
        if not request.user.is_staff:
            return Response({"error": "Not authorized."}, status=403)

        target = User.objects.filter(pk=user_id).first()
        if not target:
            return Response({"error": "User not found."}, status=404)

        action = request.data.get("action")  # "approve" or "reject"
        if action == "approve":
            from django.utils import timezone
            target.is_verified = True
            target.verification_status = "approved"
            target.verified_at = timezone.now()
            target.save(update_fields=["is_verified", "verification_status", "verified_at"])
            try:
                from notifications.utils import create_notification
                create_notification(target, "system", "🎉 Congratulations! Your account is now verified ✅", link="/user/profile")
            except Exception:
                pass
            return Response({"status": "approved"})

        elif action == "reject":
            # Refund the 500 points
            try:
                from wallet.utils import award_points
                award_points(target, 500, "admin_adjust", "Verification application refunded")
            except Exception:
                pass
            target.verification_status = "rejected"
            target.save(update_fields=["verification_status"])
            try:
                from notifications.utils import create_notification
                create_notification(target, "system", "Your verification application was not approved. 500 pts refunded.", link="/user/profile")
            except Exception:
                pass
            return Response({"status": "rejected"})

        return Response({"error": "Invalid action."}, status=400)


class PendingVerificationsView(APIView):
    """Staff-only: list all pending verification applications."""
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request):
        if not request.user.is_staff:
            return Response({"error": "Not authorized."}, status=403)
        users = User.objects.filter(verification_status="pending").values(
            "id", "username", "email", "neighborhood", "date_joined", "followers_count"
        )
        return Response(list(users))
