import logging
from datetime import datetime
from typing import Any

from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db import transaction
from django.db.models import Q
from django.db.models.base import Model as Model
from django.db.models.query import QuerySet
from django.http import JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse_lazy
from django.views import generic
from django_datatables_view.base_datatable_view import BaseDatatableView

from module_iam import iam_constant, permission
from module_project import constants
from module_project.mixins import ActionMixin
from module_project.utils import JsonResponseUtil

from .forms import (CustomAuthenticationForm, IAmPrincipalForm, IAmPrincipalResourceLinkForm,
                    IAmPrincipalGroupLinkForm, IAmPrincipalGroupRoleLinkForm,
                    IAmPrincipalRoleAppResourceActionLinkForm, ProfileEditForm)
from .models import (IAmAppResourceActionLink, IAmPrincipal, IAmPrincipalGroup,
                     IAmPrincipalType, IAmRole)

logger = logging.getLogger(__name__)

# Create your views here.


class DashboardView(LoginRequiredMixin, generic.TemplateView):
    page_name = iam_constant.RESOURCE_MANAGE_DASHBOARD
    resource = iam_constant.RESOURCE_MANAGE_DASHBOARD
    template_name = "base_structure/layout/dashboard.html"

    def get_user_count(self):
        obj = IAmPrincipal.objects.all()
        # Count active users
        active_user_count = obj.filter(is_active=True).count()
        # Count total users
        total_user_count = obj.count()
        return active_user_count, total_user_count

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        active_user_count, total_user_count = self.get_user_count()
        context["active_user_count"] = active_user_count
        context["total_user_count"] = total_user_count
        context["page_name"] = self.page_name
        return context


class PrincipalCreateOrUpdateView(permission.ResourcePermissionRequiredMixin, LoginRequiredMixin, generic.View):
    page_name = iam_constant.RESOURCE_IAM_PRINCIPAL
    resource = iam_constant.RESOURCE_IAM_PRINCIPAL
    model = IAmPrincipal
    form_class = IAmPrincipalForm
    template_name = "module_iam/iam_principal_add.html"
    success_url = reverse_lazy("module_iam:principal_group_link")
    success_message = "Saved Successfully"
    error_message = "An error occurred while saving the data."

    def get_object(self):
        pk = self.kwargs.get("pk")
        return get_object_or_404(self.model, pk=pk) if pk else None

    def get_context_data(self, **kwargs):
        context = {
            "page_name": self.page_name,
            "operation": "Edit" if self.object else "Add",
        }
        context.update(kwargs)  # Include any additional context data passed to the view
        return context

    def get(self, request, *args, **kwargs):
        self.object = self.get_object()
        form = self.form_class(instance=self.object)
        context = self.get_context_data(form=form)
        return render(request, self.template_name, context=context)

    @transaction.atomic
    def post(self, request, *args, **kwargs):
        print(request.POST)
        self.object = self.get_object()
        form = self.form_class(request.POST, instance=self.object)
        try:
            if form.is_valid():
                principal = form.save(commit=False)

                # Check if it's a new object (create action) or an existing one (update action)
                if not principal.pk:  # pk is None for new objects
                    principal.created_by = request.user
                principal.modified_by = request.user
                principal.modified_on = datetime.now()

                # Save the object
                principal.save()

                messages.success(request, "Form submitted successfully")
                return redirect(self.success_url)
        except Exception as e:
            self.error_message = constants.ERROR_OCCURR.format(str(e))
            print(self.error_message)
            messages.error(request, self.error_message)

        context = self.get_context_data(form=form)
        return render(request, template_name=self.template_name, context=context)


class PrincipalArchiveListJsonView(BaseDatatableView):
    model = IAmPrincipal
    columns = ["id", "first_name", "email", "is_active"]
    order_columns = ["id", "first_name", "email", "is_active"]

    def get_initial_queryset(self):
        deleted_flag = self.request.GET.get("deleted_flag", False)
        return self.model.objects.filter(
            deleted=deleted_flag,
            principal_type__name__in=(
                iam_constant.PRINCIPAL_TYPE_ADMIN,
                iam_constant.PRINCIPAL_TYPE_SUBADMIN,
            ),
        )

    def render_column(self, row, column):
        if column == "principal_type_name":
            return row.principal_type.name if row.principal_type else None
        return super().render_column(row, column)

    def filter_queryset(self, qs):
        search_value = self.request.GET.get("search[value]", None)
        if search_value:
            qs = qs.filter(
                Q(id__icontains=search_value)
                | Q(first_name__icontains=search_value)
                | Q(email__icontains=search_value)
            )
        return qs

class PrincipalResourcePermissionEditView(permission.ResourcePermissionRequiredMixin, LoginRequiredMixin, generic.View):
    page_name = iam_constant.RESOURCE_IAM_PRINCIPAL
    resource = iam_constant.RESOURCE_IAM_PRINCIPAL_GROUP
    model = IAmPrincipal
    template_name = "module_iam/iam_principal_resource_permission_edit.html"
    form_class = IAmPrincipalResourceLinkForm
    success_url = reverse_lazy("module_iam:principal_group_link")
    success_message = "Record Updated Successfully"
    error_message = "An error occurred while saving the data"

    def get_object(self):
        pk = self.kwargs.get("pk")
        return get_object_or_404(self.model, pk=pk) if pk else None

    def get_context_data(self, **kwargs):
        context = {
            "page_name": self.page_name,
            "operation": "Edit",
        }
        context.update(kwargs)  # Include any additional context data passed to the view
        return context

    def get(self, request, *args, **kwargs):
        self.object = self.get_object()
        form = self.form_class(instance=self.object)
        context = self.get_context_data(form=form)
        return render(request, self.template_name, context=context)

    def post(self, request, *args, **kwargs):
        self.object = self.get_object()
        form = self.form_class(request.POST, instance=self.object)
        if not form.is_valid():
            context = self.get_context_data(form=form)
            return render(request, self.template_name, context=context)
        form.save()
        messages.success(request, self.success_message)
        return redirect(self.success_url)


class PrincipalGroupLinkView(permission.ResourcePermissionRequiredMixin, LoginRequiredMixin, generic.TemplateView):
    page_name = iam_constant.RESOURCE_IAM_PRINCIPAL_GROUP
    resource = iam_constant.RESOURCE_IAM_PRINCIPAL_GROUP
    model = IAmPrincipal
    template_name = "module_iam/iam_principal_group_link.html"

    def get_context_data(self, **kwargs) -> dict[str, Any]:
        context = super().get_context_data(**kwargs)
        context["page_name"] = self.page_name
        return context

class PrincipalArchiveView(PrincipalGroupLinkView):
    page_name = iam_constant.RESOURCE_IAM_PRINCIPAL
    resource = iam_constant.RESOURCE_IAM_PRINCIPAL
    action = None
    template_name = "module_iam/iam_principal_archive.html"


class PrincipalGroupLinkAdminListJsonView(BaseDatatableView):
    model = IAmPrincipal
    columns = ["id", "first_name", "email", "is_active"]
    order_columns = ["id", "first_name", "email", "is_active"]

    def get_initial_queryset(self):
        deleted_flag = self.request.GET.get("deleted_flag", False)
        return self.model.objects.filter(
            deleted=deleted_flag, principal_type__name=iam_constant.PRINCIPAL_TYPE_ADMIN
        )

    def render_column(self, row, column):
        if column == "principal_type_name":
            return row.principal_type.name if row.principal_type else None
        return super().render_column(row, column)

    def filter_queryset(self, qs):
        search_value = self.request.GET.get("search[value]", None)
        if search_value:
            qs = qs.filter(
                Q(id__icontains=search_value)
                | Q(first_name__icontains=search_value)
                | Q(email__icontains=search_value)
            )
        return qs


class PrincipalGroupLinkSubAdminListJsonView(permission.ResourcePermissionRequiredMixin, BaseDatatableView):
    model = IAmPrincipal
    columns = ["id", "first_name", "email", "is_active"]
    order_columns = ["id", "first_name", "email", "is_active"]

    def get_initial_queryset(self):
        deleted_flag = self.request.GET.get("deleted_flag", False)
        return self.model.objects.filter(
            deleted=deleted_flag,
            principal_type__name=iam_constant.PRINCIPAL_TYPE_SUBADMIN,
        )

    def render_column(self, row, column):
        if column == "principal_type_name":
            return row.principal_type.name if row.principal_type else None
        if column == "permission":
            return [{"name": resource.name} for resource in row.principal_resource.all()]
        return super().render_column(row, column)

    def filter_queryset(self, qs):
        search_value = self.request.GET.get("search[value]", None)
        if search_value:
            qs = qs.filter(
                Q(id__icontains=search_value)
                | Q(first_name__icontains=search_value)
                | Q(email__icontains=search_value)
            )
        return qs


class PrincipalGroupLinkEditView(permission.ResourcePermissionRequiredMixin, LoginRequiredMixin, generic.View):
    page_name = iam_constant.RESOURCE_IAM_PRINCIPAL_GROUP
    resource = iam_constant.RESOURCE_IAM_PRINCIPAL_GROUP
    model = IAmPrincipal
    template_name = "module_iam/iam_principal_group_link_edit.html"
    form_class = IAmPrincipalGroupLinkForm
    success_url = reverse_lazy("module_iam:principal_group_link")
    success_message = "Record Updated Successfully"
    error_message = "An error occurred while saving the data"

    def get_object(self):
        pk = self.kwargs.get("pk")
        return get_object_or_404(self.model, pk=pk) if pk else None

    def get_context_data(self, **kwargs):
        context = {
            "page_name": self.page_name,
            "operation": "Edit",
        }
        context.update(kwargs)  # Include any additional context data passed to the view
        return context

    def get(self, request, *args, **kwargs):
        self.object = self.get_object()
        form = self.form_class(instance=self.object)
        context = self.get_context_data(form=form)
        return render(request, self.template_name, context=context)

    def post(self, request, *args, **kwargs):
        self.object = self.get_object()
        form = self.form_class(request.POST, instance=self.object)
        if not form.is_valid():
            context = self.get_context_data(form=form)
            return render(request, self.template_name, context=context)
        form.save()
        messages.success(request, self.success_message)
        return redirect(self.success_url)


class PrincipalGroupLinkActionView(generic.View):
    model = IAmPrincipal

    def post(self, request, *args, **kwargs):

        if self.model is None:
            raise NotImplementedError(
                "Subclasses of BaseActionView must define a 'model' attribute."
            )

        action = request.POST.get("action")  # 'archive', 'active', or 'unarchive'
        ids = request.POST.getlist("ids[]")  # List of IDs to perform action on
        active = request.POST.get("active")
        print(f"arhive action {action} and id is {ids} and active data is {active}")
        if action == "archive":
            # Update 'deleted' field to True for the selected users
            self.model.objects.filter(id__in=ids).update(deleted=True, is_active=False)
            message = "Record archived successfully."
        elif action == "active":
            # Update 'active' field to True for the selected users
            self.model.objects.filter(id__in=ids).update(is_active=active.capitalize())
            message = "Record updated successfully."
        elif action == "unarchive":
            # Update 'deleted' field to False for the selected users
            self.model.objects.filter(id__in=ids).update(deleted=False)
            message = "Record unarchived successfully."
        else:
            return JsonResponseUtil.error(message="Invalid Action")

        return JsonResponseUtil.success(message=message)


class PrincipalGroupView(permission.ResourcePermissionRequiredMixin, LoginRequiredMixin, generic.TemplateView):
    page_name = iam_constant.RESOURCE_IAM_GROUP
    resource = iam_constant.RESOURCE_IAM_GROUP
    model = IAmPrincipalGroup
    template_name = "module_iam/iam_group.html"

    def get_context_data(self, **kwargs) -> dict[str, Any]:
        context = super().get_context_data(**kwargs)
        context["page_name"] = self.page_name
        return context


class PrincipalGroupListJsonView(BaseDatatableView):
    model = IAmPrincipalGroup
    columns = ["id", "name", "active"]
    order_columns = ["id", "name", "active"]

    def get_initial_queryset(self):
        deleted_flag = self.request.GET.get("deleted_flag", False)
        return self.model.objects.filter(deleted=deleted_flag)

    def render_column(self, row, column):
        if column == "roles":
            return [{"name": role.name} for role in row.role.all()]
        return super().render_column(row, column)

    def filter_queryset(self, qs):
        search_value = self.request.GET.get("search[value]", None)
        if search_value:
            qs = qs.filter(
                Q(id__icontains=search_value) | Q(name__icontains=search_value)
            )
        return qs


class PrincipalGroupCreateOrUpdateView(permission.ResourcePermissionRequiredMixin, LoginRequiredMixin, generic.View):
    page_name = iam_constant.RESOURCE_IAM_GROUP
    resource = iam_constant.RESOURCE_IAM_GROUP
    page_title = "Principal Group"
    model = IAmPrincipalGroup
    template_name = "module_iam/iam_group_add.html"
    form_class = IAmPrincipalGroupRoleLinkForm
    success_url = reverse_lazy("module_iam:principal_group")
    error_message = "An error occurred while saving the data."

    def get_success_message(self):
        self.success_message = (
            constants.RECORD_CREATED if not self.object else constants.RECORD_UPDATED
        )
        return self.success_message

    def get_object(self):
        pk = self.kwargs.get("pk")
        return get_object_or_404(self.model, pk=pk) if pk else None

    def get_context_data(self, **kwargs):
        context = {
            "page_name": self.page_name,
            "operation": "Add" if not self.object else "Edit",
        }
        context.update(kwargs)  # Include any additional context data passed to the view
        return context

    def get(self, request, *args, **kwargs):
        self.object = self.get_object()
        form = self.form_class(instance=self.object)
        context = self.get_context_data(form=form)
        return render(request, self.template_name, context=context)

    def post(self, request, *args, **kwargs):
        self.object = self.get_object()
        form = self.form_class(request.POST, instance=self.object)
        if not form.is_valid():
            print(form.errors)
            context = self.get_context_data(form=form)
            return render(request, self.template_name, context=context)
        form.save()
        messages.success(self.request, self.get_success_message())
        return redirect(self.success_url)


class PrincipalGroupActionView(ActionMixin):
    model = IAmPrincipalGroup


class PrincipalGroupArchiveView(PrincipalGroupView):
    template_name = "module_iam/iam_group_archive_list.html"

class AppRoleView(permission.ResourcePermissionRequiredMixin, LoginRequiredMixin, generic.TemplateView):
    page_name = iam_constant.RESOURCE_IAM_ROLE
    resource = iam_constant.RESOURCE_IAM_ROLE
    model = IAmRole
    template_name = "module_iam/iam_role.html"

    def get_context_data(self, **kwargs) -> dict[str, Any]:
        context = super().get_context_data(**kwargs)
        context["page_name"] = self.page_name
        return context


class AppRoleListJsonView(BaseDatatableView):
    model = IAmRole
    columns = ["id", "name", "active", "resources"]
    order_columns = ["id", "name"]

    def get_initial_queryset(self):
        deleted_flag = self.request.GET.get("deleted_flag", False)
        return (
            super(AppRoleListJsonView, self)
            .get_initial_queryset()
            .prefetch_related(
                "app_resource_action",
                "app_resource_action__app_resource",
                "app_resource_action__app_action",
            )
            .filter(deleted=deleted_flag)
        )

    def render_column(self, row, column):
        if column == "resources":
            resources = {}
            # Loop through all the app_resource_action links for the current ro
            for link in row.app_resource_action.all():
                resource = link.app_resource.name
                action = link.app_action.name
                # If the resource is already in the dictionary, append the action to the list of actions
                if resource in resources:
                    resources[resource].append(action)
                # Otherwise, add the resource to the dictionary with a list containing the action
                else:
                    resources[resource] = [action]
            return resources
        return super().render_column(row, column)

    def filter_queryset(self, qs):
        search_value = self.request.GET.get("search[value]", None)
        if search_value:
            qs = qs.filter(
                Q(id__icontains=search_value)
                | Q(name__icontains=search_value)
                | Q(app_resource_action__app_resource__name__icontains=search_value)
                | Q(app_resource_action__app_action__name__icontains=search_value)
            )
        return qs


class AppRoleCreateOrUpdateView(permission.ResourcePermissionRequiredMixin, LoginRequiredMixin, generic.View):
    page_name = iam_constant.RESOURCE_IAM_ROLE
    resource = iam_constant.RESOURCE_IAM_ROLE
    model = IAmRole
    template_name = "module_iam/iam_role_add.html"
    form_class = IAmPrincipalRoleAppResourceActionLinkForm
    success_url = reverse_lazy("module_iam:role")
    success_message = "Saved Successfully"
    error_message = "An error occurred while saving the data."

    def get_success_message(self):
        self.success_message = (
            f"Record {'Created' if not self.object else 'Updated'} Successfully"
        )
        return self.success_message

    def get_object(self):
        pk = self.kwargs.get("pk")
        return get_object_or_404(self.model, pk=pk) if pk else None

    def get_context_data(self, **kwargs):
        context = {
            "page_name": self.page_name,
            "operation": "Add" if not self.object else "Edit",
            "app_resource_action": IAmAppResourceActionLink.objects.generate_app_resource_action_data(),
        }
        context.update(kwargs)  # Include any additional context data passed to the view
        return context

    def get(self, request, *args, **kwargs):
        try:
            self.object = self.get_object()
            form = self.form_class(instance=self.object)
            context = self.get_context_data(form=form)
            return render(request, self.template_name, context=context)
        except Exception as e:
            messages.error(request, str(e))
            return redirect(self.success_url)

    def post(self, request, *args, **kwargs):
        try:
            self.object = self.get_object()
            form = self.form_class(request.POST, instance=self.object)
            if not form.is_valid():
                print(form.errors)
                context = self.get_context_data(form=form)
                return render(request, self.template_name, context=context)
            form.save()
            messages.success(self.request, self.get_success_message())
            return redirect(self.success_url)
        except Exception as e:
            messages.error(self.request, str(e))
            return redirect(self.success_url)


class AppRoleActionView(LoginRequiredMixin, ActionMixin):
    model = IAmRole


class AppRoleArchiveView(AppRoleView):
    template_name = "module_iam/iam_role_archive.html"


class PrincipalProfileView( LoginRequiredMixin, generic.TemplateView):
    page_name = iam_constant.RESOURCE_MANAGE_DASHBOARD
    resource = iam_constant.RESOURCE_MANAGE_DASHBOARD
    model = IAmPrincipal
    template_name = "module_iam/profile_details.html"

    def get_object(self, queryset=None):
        user = self.request.user.id
        return get_object_or_404(
            self.model.objects.select_related("principal_type", "principal_source"),
            pk=user,
        )

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["page_name"] = self.page_name
        context["data_obj"] = self.get_object()
        return context


class PrincipalProfileEditView(LoginRequiredMixin, generic.View):
    page_name = iam_constant.RESOURCE_MANAGE_DASHBOARD
    resource = iam_constant.RESOURCE_MANAGE_DASHBOARD
    model = IAmPrincipal
    template_name = "module_iam/profile_details_edit.html"
    form_class = ProfileEditForm
    success_url = reverse_lazy("module_iam:profile_details")
    success_message = "Saved Successfully"
    error_message = "An error occurred while saving the data."

    def get_success_message(self):
        self.success_message = (
            f"Record {'Created' if not self.object else 'Updated'} Successfully"
        )
        return self.success_message

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

    def get_context_data(self, **kwargs):
        context = {
            # "page_name": self.page_name,
            "operation": "Edit",
            "page_name": self.page_name,
        }
        context.update(kwargs)  # Include any additional context data passed to the view
        return context

    def get(self, request, *args, **kwargs):
        # try:
        self.object = self.get_object()
        form = self.form_class(instance=self.object)
        context = self.get_context_data(form=form)
        return render(request, self.template_name, context=context)

    def post(self, request, *args, **kwargs):
        self.object = self.get_object()
        form = self.form_class(request.POST, request.FILES, instance=self.object)
        if not form.is_valid():
            print(form.errors)
            context = self.get_context_data(form=form)
            return render(request, self.template_name, context=context)

        form.save()
        messages.success(self.request, self.get_success_message())
        return redirect(self.success_url)
