# users/views.py
import os
from django.utils.crypto import get_random_string
from rest_framework.generics import CreateAPIView
from rest_framework.views import APIView
from rest_framework import permissions, status, viewsets
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework_simplejwt.tokens import RefreshToken
from rest_framework_simplejwt.authentication import JWTAuthentication
from google.oauth2 import id_token
from google.auth.transport import requests as google_requests
from dotenv import load_dotenv
from django.db import transaction
from django.shortcuts import redirect
from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string
from django.contrib.sites.shortcuts import get_current_site
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
from django.utils.encoding import force_bytes, force_str
from django.contrib.auth.tokens import default_token_generator
from django.conf import settings
from django.contrib.auth import authenticate, login
from django.urls import reverse
from rest_framework.decorators import action
from django.conf import settings
from django.db.models import F, Sum


from .models import Users, Roles, User_Roles, Role_Permissions, PermissionAudit
from .serializers import (
    UsersSerializer,
    RolesSerializer,
    UserRolesSerializer,
    UserRolesCreateSerializer,
    UserSignupSerializer,
    OfficerInvitationSerializer,
    OfficerPasswordSetupSerializer,
    PermissionsSyncSerializer,
)

from .permissions import IsAdminOrITOfficer


# A viewset for the core Users model
class UsersViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows users to be viewed or edited.
    It uses the UsersSerializer to handle data and correctly hashes passwords.
    """

    queryset = Users.objects.all().order_by("-created_at")
    serializer_class = UsersSerializer
    permission_classes = [IsAuthenticated]


# A viewset for the Roles model
class RolesViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows roles to be viewed or edited.
    """

    queryset = Roles.objects.all()
    serializer_class = RolesSerializer
    permission_classes = [IsAuthenticated]


# A viewset that handles roles and has a custom action for permissions
class RolesViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows roles to be viewed, created, or edited.
    Includes a custom action to get all available permissions.
    """

    queryset = Roles.objects.all()
    serializer_class = RolesSerializer
    permission_classes = [IsAuthenticated]

    # Custom action to get all unique module_name:action pairs
    # This is the endpoint the frontend will call to populate the checkboxes
    @action(detail=False, methods=["get"], url_path="all-permissions")
    def get_all_permissions(self, request):
        # We query the distinct module_name and action pairs from the database
        permissions = Role_Permissions.objects.values(
            "module_name", "action"
        ).distinct()

        # Format the data for the frontend
        formatted_permissions = [
            {"module_name": p["module_name"], "action": p["action"]}
            for p in permissions
        ]

        return Response(formatted_permissions, status=status.HTTP_200_OK)


# A viewset for assigning roles to users for a specific tenant
class UserRolesViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows user-role assignments to be viewed, created, or edited.
    """

    queryset = User_Roles.objects.all()

    # We use a different serializer for creating/updating vs listing
    def get_serializer_class(self):
        if self.action in ["create", "update", "partial_update"]:
            return UserRolesCreateSerializer
        return UserRolesSerializer

    permission_classes = [IsAuthenticated]


# New: Public view for citizen signup
class UserSignupView(CreateAPIView):
    queryset = Users.objects.all()
    serializer_class = UserSignupSerializer
    permission_classes = [permissions.AllowAny]

    def perform_create(self, serializer):
        user = serializer.save(is_active=False)

        current_site = get_current_site(self.request)

        # Construct the activation URL
        uidb64 = urlsafe_base64_encode(force_bytes(user.pk))
        token = default_token_generator.make_token(user)
        relative_url = reverse(
            "activate-user", kwargs={"uidb64": uidb64, "token": token}
        )
        activation_link = f"http://{current_site.domain}{relative_url}"

        context = {
            "user": user,
            "activation_link": activation_link,  # Pass the complete link to the template
        }

        email_html_message = render_to_string("email_activation.html", context)
        email_subject = "Activate Your JIT Account"

        email = EmailMultiAlternatives(
            email_subject,
            f"Please activate your account by clicking this link: {activation_link}",
            settings.DEFAULT_FROM_EMAIL,
            [user.email],
        )
        email.attach_alternative(email_html_message, "text/html")
        email.send()

        return Response(
            {
                "message": "Registration successful. Please check your email to activate your account."
            },
            status=status.HTTP_201_CREATED,
        )


# New: Class-based view to activate a user account
class ActivateUserView(APIView):
    permission_classes = [permissions.AllowAny]

    def get(self, request, uidb64, token):
        try:
            uid = force_str(urlsafe_base64_decode(uidb64))
            user = Users.objects.get(pk=uid)
        except (TypeError, ValueError, OverflowError, Users.DoesNotExist):
            user = None

        if user is not None and default_token_generator.check_token(user, token):
            user.is_active = True
            user.save()
            return redirect(f"{settings.FRONTEND_URL}/")
        else:
            return redirect(f"{settings.FRONTEND_URL}/?error=activation_failed")


# New: Class-based view for user login
class LoginUserView(APIView):
    permission_classes = [permissions.AllowAny]

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

        user = authenticate(request, email=email, password=password)
        if user is not None:
            if not user.is_active:
                return Response(
                    {
                        "error": "Account is not activated. Please check your email for the activation link."
                    },
                    status=status.HTTP_401_UNAUTHORIZED,
                )
            
            # --- START: New logic to increment login count ---
            # Use F expression for atomic update to avoid race conditions
            user.login_count = F('login_count') + 1
            user.save(update_fields=['login_count'])
            user.refresh_from_db() # Refresh the instance to get the current value if needed later

            print(f"Login count incremented for user: {user.email}. New count: {user.login_count}")
            # --- END: New logic ---

            # --- Start: New logic to get permissions and tenant ID ---
            user_permissions = []
            user_tenant_id = None
            is_superuser = user.is_superuser
            print(
                f"User '{user.username}' logged in. Is superuser: {user.is_superuser}"
            )

            # Get the user's role and tenant via the User_Roles model
            user_role_assignment = User_Roles.objects.filter(user=user).first()

            if user_role_assignment:
                user_tenant_id = str(user_role_assignment.tenant.tenant_id)
                role = user_role_assignment.role

                # Get all permissions for that role
                role_permissions = Role_Permissions.objects.filter(role=role)

                user_permissions = [
                    f"{p.module_name}:{p.action}" for p in role_permissions
                ]
            # --- End: New logic ---

            refresh = RefreshToken.for_user(user)
            return Response(
                {
                    "email": user.email,
                    "is_superuser": is_superuser,
                    "user_permissions": user_permissions,
                    "tenant_id": user_tenant_id,
                    "access": str(refresh.access_token),
                    "refresh": str(refresh),
                },
                status=status.HTTP_200_OK,
            )
        else:
            return Response(
                {"error": "Invalid credentials"}, status=status.HTTP_401_UNAUTHORIZED
            )
        

class DashboardMetricsView(APIView):
    # Only authenticated users should access dashboard data
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request):
        # Calculate the total number of times all users have logged in
        total_engagement = Users.objects.aggregate(total_logins=Sum('login_count'))['total_logins'] or 0

        
        # Format the total engagement number with commas for display
        formatted_engagement = f"{total_engagement:,}"

        metrics = {
            'citizen_engagement': {
                'value': formatted_engagement,
                # Replace with real calculated data later
                'trend': 'up', 
                'trendValue': '+12%',
            },
            # You can add more metrics here for the other cards later
            
        }
        
        return Response(metrics, status=status.HTTP_200_OK)



# function to fetch permissions from db
def get_permissions_for_user(user):
    """
    Retrieves all permissions for a given user based on their role(s).
    """
    # 1. Handle superusers first
    if user.is_superuser:
        # A superuser implicitly has all permissions.
        # We can return all permissions from your custom model.
        all_permissions = Role_Permissions.objects.values(
            "module_name", "action"
        ).distinct()
        return [f"{p['module_name']}.{p['action']}" for p in all_permissions]

    # 2. Get the roles for the user by querying the junction table
    # This returns a queryset of User_Roles objects.
    user_roles_queryset = user.user_roles_set.all()

    # If the user has no roles, return an empty list
    if not user_roles_queryset.exists():
        return []

    # 3. Collect the permissions for all of the user's roles
    # We use a set to ensure unique permissions
    user_permissions = set()
    for user_role in user_roles_queryset:
        # For each role, get the associated permissions
        role_permissions = Role_Permissions.objects.filter(
            role=user_role.role
        ).values_list("module_name", "action", named=True)

        for p in role_permissions:
            user_permissions.add(f"{p.module_name}.{p.action}")

    # 4. Return the unique permissions as a list
    return list(user_permissions)


class CurrentUserView(APIView):
    """
    Returns the current user's details, including permissions and superuser status.
    This view requires a valid JWT access token for authentication.
    """

    authentication_classes = [JWTAuthentication]  # Use JWT authentication
    permission_classes = [IsAuthenticated]  # Ensure only authenticated users can access

    def get(self, request):
        user = request.user

        # Log to the terminal to confirm this is being hit
        print(
            f"GET request to /api/user/me/. User is '{user.email}'. Is superuser: {user.is_superuser}"
        )
        # Get all roles and tenants associated with the user
        user_roles_with_tenants = list(
            user.user_roles_set.all().values(
                "role_id", "role__role_name", "tenant_id", "tenant__name"
            )
        )
        # Collect all user permissions
        print(user_roles_with_tenants)
        # Use the custom function to get permissions
        user_permissions = get_permissions_for_user(user)
        print(f"User permissions for current user: {user_permissions}")
        print(user_permissions)

        # Return the user data in the response
        response_data = {
            "username": user.username,
            "email": user.email,
            "is_superuser": user.is_superuser,
            "user_permissions": user_permissions,
            "user_roles_set": user_roles_with_tenants,
            # Add any other user data you need here
        }
        return Response(response_data, status=status.HTTP_200_OK)


GOOGLE_CLIENT_ID = os.getenv("GOOGLE_CLIENT_ID")
GOOGLE_CLIENT_ID = (
    "764706231535-61o8hs5bui31at36psk5ar8kbcfvsmnj.apps.googleusercontent.com"
)


def get_user_from_google_token(access_token):
    """
    Validates a Google token and returns the user info.
    """
    try:
        # Specify the CLIENT_ID of the app that accesses the backend
        idinfo = id_token.verify_oauth2_token(
            access_token, google_requests.Request(), GOOGLE_CLIENT_ID
        )
        return idinfo
    except ValueError:
        return None


class GoogleAuthView(APIView):
    """
    Handles login and signup via a Google ID token.
    """

    permission_classes = [permissions.AllowAny]

    def post(self, request, *args, **kwargs):
        access_token = request.data.get("access_token")
        if not access_token:
            return Response(
                {"error": "Google access token not provided."},
                status=status.HTTP_400_BAD_REQUEST,
            )

        idinfo = get_user_from_google_token(access_token)
        if not idinfo:
            return Response(
                {"error": "Invalid Google token."}, status=status.HTTP_400_BAD_REQUEST
            )

        email = idinfo.get("email")
        username = email
        try:
            # Try to get the existing user
            user = Users.objects.get(email=email)
        except Users.DoesNotExist:
            # Create a new user if one doesn't exist
            randompassword = get_random_string(16)
            user = Users.objects.create_user(
                username=username,
                email=email,
                password=randompassword,
            )

        # --- Start: New logic to get permissions and tenant ID ---
        user_permissions = []
        user_tenant_id = None
        is_superuser = user.is_superuser

        # Get the user's role and tenant via the User_Roles model
        user_role_assignment = User_Roles.objects.filter(user=user).first()
        if user_role_assignment:
            user_tenant_id = str(user_role_assignment.tenant.tenant_id)
            role = user_role_assignment.role

            # Get all permissions for that role
            role_permissions = Role_Permissions.objects.filter(role=role)
            user_permissions = [f"{p.module_name}:{p.action}" for p in role_permissions]
        # --- End: New logic ---

        # Generate a new JWT for the user
        refresh = RefreshToken.for_user(user)
        return Response(
            {
                "username": user.username,
                "is_superuser": is_superuser,
                "user_permissions": user_permissions,
                "tenant_id": user_tenant_id,
                "access": str(refresh.access_token),
                "refresh": str(refresh),
            },
            status=status.HTTP_200_OK,
        )


# View to handle password reset requests (sending the email)
class PasswordResetView(APIView):
    permission_classes = [permissions.AllowAny]

    def post(self, request):
        email = request.data.get("email")
        try:
            user = Users.objects.get(email=email)
        except Users.DoesNotExist:
            return Response(
                {"detail": "User with this email address not found."},
                status=status.HTTP_404_NOT_FOUND,
            )

        current_site = get_current_site(self.request)
        uidb64 = urlsafe_base64_encode(force_bytes(user.pk))
        token = default_token_generator.make_token(user)

        reset_url = f"{settings.FRONTEND_URL}/password-reset-confirm/{uidb64}/{token}"

        context = {
            "user": user,
            "reset_url": reset_url,
        }

        email_html_message = render_to_string("password_reset_email.html", context)
        email_subject = "Password Reset Request"
        email = EmailMultiAlternatives(
            email_subject,
            f"Please click the following link to reset your password: {reset_url}",
            settings.DEFAULT_FROM_EMAIL,
            [user.email],
        )
        email.attach_alternative(email_html_message, "text/html")
        email.send()

        return Response(
            {"detail": "Password reset email has been sent."}, status=status.HTTP_200_OK
        )


# View to confirm the password reset and set the new password
class PasswordResetConfirmView(APIView):
    permission_classes = [permissions.AllowAny]

    def post(self, request, uidb64, token):
        try:
            uid = force_str(urlsafe_base64_decode(uidb64))
            user = Users.objects.get(pk=uid)
        except (TypeError, ValueError, OverflowError, Users.DoesNotExist):
            return Response(
                {"detail": "Invalid token or user ID."},
                status=status.HTTP_400_BAD_REQUEST,
            )

        if default_token_generator.check_token(user, token):
            new_password = request.data.get("new_password")
            if not new_password:
                return Response(
                    {"detail": "New password is required."},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            user.set_password(new_password)
            user.save()
            return Response(
                {"detail": "Password has been reset successfully."},
                status=status.HTTP_200_OK,
            )
        else:
            return Response(
                {"detail": "Invalid or expired token."},
                status=status.HTTP_400_BAD_REQUEST,
            )


# Alternative approach using a custom permission class (cleaner)
class HasCreateUserPermission:
    """
    Custom permission class specifically for checking Create User permission.
    """

    def has_permission(self, request, view):
        user = request.user
        if not user or not user.is_authenticated:
            return False

        required_permission = "Create User.users.add_users"
        user_permissions = get_permissions_for_user(user)
        return required_permission in user_permissions


class OfficerInviteView(APIView):
    """
    Alternative implementation using a custom permission class.
    This is cleaner and more reusable.
    """

    authentication_classes = [JWTAuthentication]
    permission_classes = [IsAuthenticated, HasCreateUserPermission]

    def post(self, request):
        # Log the permission check for debugging
        user = request.user
        user_permissions = get_permissions_for_user(user)
        print(
            f"User '{user.username}' attempting to invite officer. Permissions: {user_permissions}"
        )

        serializer = OfficerInvitationSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        user = serializer.create(serializer.validated_data)

        # After creating the user, generate a token and send the email
        current_site = get_current_site(request)
        uidb64 = urlsafe_base64_encode(force_bytes(user.pk))
        token = default_token_generator.make_token(user)

        password_setup_url = (
            f"{settings.FRONTEND_URL}/officer/password-setup/{uidb64}/{token}/"
        )

        context = {
            "user": user,
            "password_setup_url": password_setup_url,
        }

        email_html_message = render_to_string("officer_invitation.html", context)
        email_subject = "Activate Your Officer Account"

        email = EmailMultiAlternatives(
            email_subject,
            f"You have been invited to an officer account. Please set your password by clicking this link: {password_setup_url}",
            settings.DEFAULT_FROM_EMAIL,
            [user.email],
        )
        email.attach_alternative(email_html_message, "text/html")
        email.send()

        return Response(
            {
                "message": "Invitation sent successfully. The officer can now set their password."
            },
            status=status.HTTP_201_CREATED,
        )


class OfficerPasswordSetupView(APIView):
    """
    API endpoint for an invited officer to set their password for the first time.
    This view is public and receives a UID and token from an email link.
    """

    permission_classes = [permissions.AllowAny]

    def post(self, request, uidb64, token):
        """
        Handles the password setup logic.
        - Validates the user and token.
        - Validates the password using the serializer.
        - Sets the new password and activates the user.
        """
        try:
            # Decode the base64 user ID from the URL
            uid = force_str(urlsafe_base64_decode(uidb64))
            user = Users.objects.get(pk=uid)
        except (TypeError, ValueError, OverflowError, Users.DoesNotExist):
            # If the user doesn't exist or the UID is invalid
            return Response(
                {"detail": "Invalid token or user ID."},
                status=status.HTTP_400_BAD_REQUEST,
            )

        # Check if the provided token is valid for the user
        if default_token_generator.check_token(user, token):
            serializer = OfficerPasswordSetupSerializer(data=request.data)
            serializer.is_valid(raise_exception=True)

            # Get the new password from the validated data
            new_password = serializer.validated_data["password"]

            # Set the user's password, activate the account, and save
            user.set_password(new_password)
            user.is_active = True
            user.save()

            return Response(
                {"detail": "Password has been set successfully. You can now log in."},
                status=status.HTTP_200_OK,
            )
        else:
            # If the token is invalid or expired
            return Response(
                {"detail": "Invalid or expired token."},
                status=status.HTTP_400_BAD_REQUEST,
            )


def flatten_modules(modules):
    """
    Flatten the frontend moduleRoutes into a list of dicts with module_name and action.
    """
    flattened = []

    def recurse(module_list):
        for module in module_list:
            name = module.get("name")
            perms = module.get("permissions", [])
            for perm in perms:
                flattened.append({"module_name": name, "action": perm})
            if module.get("children"):
                recurse(module["children"])

    recurse(modules)
    return flattened


class SyncPermissionsAPIView(APIView):
    """
    Sync frontend moduleRoutes into Role_Permissions.
    - Superadmins insert global permissions.
    - Tenant admins insert tenant-specific permissions.
    """

    def post(self, request):

        serializer = PermissionsSyncSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        modules = serializer.validated_data["permissions"]

        user = request.user
        added_permissions = []

        with transaction.atomic():
            # Superadmin role for global permissions
            superadmin_role, _ = Roles.objects.get_or_create(role_name="SuperAdmin")

            # Determine tenant context
            tenant = None
            if not user.is_superuser:
                # Tenant-admin: get their tenant
                user_role_assignment = User_Roles.objects.filter(user=user).first()
                if not user_role_assignment:
                    return Response(
                        {"error": "You have no tenant role assigned"},
                        status=status.HTTP_403_FORBIDDEN,
                    )
                tenant = user_role_assignment.tenant

            for perm in modules:
                # Decide which roles to assign to
                if user.is_superuser:
                    roles_to_assign = [superadmin_role]
                else:
                    roles_under_tenant = Roles.objects.filter(
                        user_roles__tenant=tenant
                    ).distinct()
                    if not roles_under_tenant.exists():
                        default_role, _ = Roles.objects.get_or_create(
                            role_name=f"{tenant.name}Role"
                        )
                        roles_under_tenant = [default_role]
                    roles_to_assign = roles_under_tenant

                # Insert permissions for all relevant roles
                for role in roles_to_assign:
                    role_perm_obj, created = Role_Permissions.objects.get_or_create(
                        role=role,
                        module_name=perm["module_name"],
                        action=perm["action"],
                    )
                    if created:
                        added_permissions.append(
                            f"{role.role_name}:{perm['module_name']}:{perm['action']}"
                        )
                        # Create audit log only if the permission was newly created
                        PermissionAudit.objects.create(
                            role_permission=role_perm_obj,
                            created_by=user,
                        )

        return Response(
            {
                "message": "Permissions sync completed",
                "new_permissions_added": len(added_permissions),
                "permissions": added_permissions,
            },
            status=status.HTTP_200_OK,
        )
