import copy
import threading
import pandas as pd
import csv
import io, os
from app.entities import enums
from django.db import transaction
from app.helper import decorators
from rest_framework.views import APIView
from app.helper.email_service import sender
from rest_framework import viewsets, status
from rest_framework.decorators import action
from django.contrib.auth import authenticate
from django.contrib.auth.hashers import check_password
from rest_framework.response import Response
from app.applicationlayer.utils import model_to_dict
from app.entities.models import (
    User, EntityLog, PasswordReset, Application, UserImage,
    ChangeRequestFormHeader, ChangeRequestTemplateHeader,
    AllowedCompany, Company, Department, UserHistory
)
from app.helper.decorators import rms, error_safe
from django.contrib.auth.hashers import make_password
from django_filters import rest_framework as filters
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter, OrderingFilter
from app.applicationlayer.management.account.table_filters import AccountFilterset
from app.applicationlayer.management.account import serializer
from app.helper.file_manager import FileHelper
from django.db import IntegrityError
from app.applicationlayer.utils import (
    CustomPagination, status_message_response, log_save,
    main_threading
)
from rest_framework.exceptions import ParseError
from django.db.models import Q


class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all().order_by('-id')
    serializer_class = serializer.UserSerializer
    pagination_class = CustomPagination
    lookup_field = 'code'
    filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
    filterset_class = AccountFilterset
    ordering_fields = (
        'department__company__name', 'department__name',
        'name', 'code',
        'email', 'contact_no'
    )
    search_fields = (
        'name', 'code',
        'department__company__name',
        'department__name',
        'email', 'contact_no'
    )

    @rms.user_create
    @transaction.atomic
    def create(self, request, *args, **kwargs):

        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)

        password = User.objects.make_random_password(length=10)
        password_hash = make_password(password)

        app = request.data['application']

        if request.data['user_type'] != 'USR':
            rms = Application.objects.filter(id=1).values('code').first()
            app.append(rms['code'])



        app = Application.objects.filter(
            code__in=app
        )

        instance = User.objects.get(
            id=serializer.data['id']
        )


        instance.application.set(app)

        User.objects.filter(
            id=serializer.data['id']
        ).update(password=password_hash)

        message = status_message_response(
            201, 'success',
            'New Users created', serializer.data
        )

        name = request.data['name']
        username = request.data['username']
        account_email = request.data['email']
        admin_email = request.user.email
        args = [name, username, password, account_email, admin_email]

        main_threading(args, sender.account_created)

        return Response(
            message,
            status=status.HTTP_201_CREATED
        )

    @decorators.rms.user_list
    def list(self, request, *args, **kwargs):

        queryset = self.filter_queryset(self.get_queryset())
        queryset = queryset.exclude(id=1)
        page = self.paginate_queryset(queryset)

        if page is not None:
            serializer = self.get_serializer(page, many=True)

            message = status_message_response(
                200,
                'success',
                'list of Users found',
                serializer.data
            )

            return self.get_paginated_response(message)

        serializer = self.get_serializer(queryset, many=True)

        return Response(
            serializer.data,
            status=status.HTTP_200_OK
        )


    @decorators.rms.user_list
    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return Response(serializer.data)


    # @rms.user_create
    @error_safe
    @transaction.atomic
    def destroy(self, request, *args, **kwargs):

        instance = self.get_object()
        new_instance = model_to_dict(instance)
        UserHistory.objects.filter(username=instance.username).update(deleted=True)
        AllowedCompany.objects.filter(id_number=instance.code).delete()
        self.perform_destroy(instance)

        log_save(
            enums.LogEnum.DELETED.value,
            enums.LogEntitiesEnum.USER.value,
            new_instance['id'],
            new_instance,
            ''
        )

        return Response(status=status.HTTP_204_NO_CONTENT)

    @rms.user_create
    @transaction.atomic
    def update(self, request, *args, **kwargs):

        partial = kwargs.pop('partial', False)
        instance = self.get_object()


        cms_form = ChangeRequestFormHeader.objects.filter(
            Q(frm_approvers__user__code=instance.code) |
            Q(frm_stakes__user__code=instance.code) |
            Q(requested_by_user__code=instance.code) |
            Q(requested_to_user__code=instance.code)
        )

        cms_template = ChangeRequestTemplateHeader.objects.filter(
            Q(created_by_user__code=instance.code) |
            Q(requested_to_user__code=instance.code) 
        )
        if instance.department.code != request.data['department']:

            if cms_form.count() > 0 or cms_template.count() > 0:

                raise ParseError(
                    'Cannot update this record the user has a record on change request it might cause a data error'
                )

        serializer = self.get_serializer(
            instance, data=request.data, partial=partial
        )
        serializer.is_valid(raise_exception=True)

        old_instance = model_to_dict(instance)
        self.perform_update(serializer)

        app = request.data['application']

        if request.data['user_type'] != 'USR':
            rms = Application.objects.filter(id=1).values('code').first()
            app.append(rms['code'])
        # else:
        #     app = request.data['application']



        app = Application.objects.filter(
            code__in=app
        )

        instance = User.objects.get(
            id=serializer.data['id']
        )


        instance.application.set(app)
        new_instance = serializer.data

        log_save(
            enums.LogEnum.UPDATE.value,
            enums.LogEntitiesEnum.USER.value,
            new_instance['id'],
            old_instance,
            new_instance
        )

        return Response(
            serializer.data,
            status=status.HTTP_200_OK
        )


    # @rms.user_create
    # @action(detail=False,
    #         methods=['put'],
    #         url_path='batch-upload',
    #         name="upload User")
    # @decorators.error_safe
    # @transaction.atomic
    # def BatchUpload(self, request):
    #     csv_file = request.FILES['file']
    #     df = pd.read_csv(csv_file, sep=',', skiprows=0)
    #     logged_user_type = request.user.user_type
    #     logged_user_company = request.user.department.company.name
    #     logged_user_department = request.user.department.name
    #     logged_user_email = request.user.email
    #     email_users = []

    #     for data, keys in df.iterrows():
    #         try:
    #             user_department = Department.objects.filter(
    #                 Q(name__icontains=keys['department']) &
    #                 Q(company__name__icontains=logged_user_company)
    #             ).first()
    #             if logged_user_type == 'CUA':
    #                 user_department = Department.objects.filter(
    #                     Q(name__icontains=keys['department']) &
    #                     Q(company__name__icontains=logged_user_company)
    #                 ).first()
    #             if user_department == None:
    #                 msg = f"company is not the same with the logged user at row {data + 2}"
    #                 return Response(
    #                     {"message": msg},
    #                     status=status.HTTP_400_BAD_REQUEST
    #                 )
    #             elif logged_user_type == 'DUA':
    #                 user_department = Department.objects.get(
    #                     name__icontains=logged_user_department
    #                 )
    #             elif logged_user_type == 'USR':
    #                 return Response(
    #                         {"message": "Logged User is not allowed"},
    #                         status=status.HTTP_400_BAD_REQUEST
    #                     )
    #             else:
    #                 user_department = Department.objects.get(
    #                     name__icontains=keys['department']
    #                 )

    #             default_app = Application.objects.filter(
    #                 excel_code=keys['default_app']
    #             ).first()

    #             enums_super = enums.UserTypeEnum.SUPER_USER.value
    #             enums_OUA = enums.UserTypeEnum.OVERALL_USER_ADMIN.value
    #             enums_company = enums.UserTypeEnum.COMPANY_USER_ADMIN.value
    #             enums_department = enums.UserTypeEnum.DEPARTMENT_USER_ADMIN.value
    #             enums_user = enums.UserTypeEnum.USER.value

    #             if keys['user_type'].lower() == 'super user' and logged_user_type == enums_super:
    #                 user_type = enums.UserTypeEnum.SUPER_USER.value
    #             elif keys['user_type'].lower() == 'super user' and logged_user_type != enums_super:
    #                 return Response(
    #                     {"message": f"This user is not allowed to create super user. data error at row {data + 2}"},
    #                     status=status.HTTP_201_CREATED
    #                 )
    #             elif keys['user_type'].lower() == 'overall user admin':
    #                 user_type = enums.UserTypeEnum.OVERALL_USER_ADMIN.value
    #             elif keys['user_type'].lower() == 'company user admin':
    #                 user_type = enums.UserTypeEnum.COMPANY_USER_ADMIN.value
    #             elif keys['user_type'].lower() == 'department user admin':
    #                 user_type = enums.UserTypeEnum.DEPARTMENT_USER_ADMIN.value
    #             else:
    #                 user_type = enums.UserTypeEnum.USER.value
    #             users = {
    #                 "username": keys['username'],
    #                 "name": keys['name'],
    #                 "department": user_department,
    #                 "email": keys['email'],
    #                 "contact_no": keys['contact_no'],
    #                 "default_app": default_app,
    #                 "user_type": user_type
    #             }
    #             current_user = User.objects.create(
    #                 **users
    #             )

    #             password = User.objects.make_random_password(length=10)
    #             password_hash = make_password(password)
    #             current_user.password = password_hash
    #             current_user.save()

    #             app = Application.objects.filter(
    #                 excel_code__in=keys['application'].split(',')
    #             )

    #             update_user = current_user.application.set(app)

    #             for instance in keys['privilege'].split(';'):

    #                 privilege_list = instance.split(',')

    #                 this_company = Company.objects.filter(
    #                     name__icontains=privilege_list[0]
    #                 ).first()

    #                 this_department = Department.objects.filter(
    #                     name__icontains=privilege_list[1]
    #                 ).first()

    #                 if privilege_list[2] == 0:
    #                     privilege_list[2] = False
    #                 else:
    #                     privilege_list[2] = True

    #                 if privilege_list[3] == 0:
    #                     privilege_list[3] = False
    #                 else:
    #                     privilege_list[3] = True

    #                 if privilege_list[4] == 0:
    #                     privilege_list[4] = False
    #                 else:
    #                     privilege_list[4] = True

    #                 current_user = User.objects.get(id=current_user.id)
    #                 try:
    #                     privilege_object = {
    #                         "id_number": current_user,
    #                         "company_pivot": this_company,
    #                         "group_pivots": this_department,
    #                         "create_change_request": privilege_list[2],
    #                         "create_change_request_template": privilege_list[3],
    #                         "view_all_change_request": privilege_list[4],
    #                         "approve_cr": privilege_list[5]
    #                     }
    #                     AllowedCompany.objects.create(**privilege_object)
    #                 except IntegrityError as e:
    #                     return Response(
    #                         {"message": f"Duplicate user privilege at row {data + 2}"},
    #                         status=status.HTTP_400_BAD_REQUEST
    #                     )

    #         except IntegrityError as e:
    #             return Response(
    #                 {"message": f"Record already exist at row {data + 2}"},
    #                 status=status.HTTP_400_BAD_REQUEST
    #             )

    #         except KeyError as e:
    #             return Response(
    #                 {"message": "Missing column user_type"},
    #                 status=status.HTTP_400_BAD_REQUEST
    #             )

    #         del users['department']
    #         del users['contact_no']
    #         del users['default_app']
    #         del users['user_type']

    #         users['password'] = password

    #         UserHistory.objects.create(
    #             **users
    #         )
    #         users['admin'] = logged_user_email


    #     send_mail = UserHistory.objects.filter(sent=False)[0:100].values(
    #         'name', 'username', 'email', 'password'
    #     )

    #     df = pd.DataFrame(send_mail)
    #     df.to_csv("users.csv", index=False)


    #     args = ["users.csv", logged_user_email]
    #     main_threading(args, sender.batch_email_admin)

    #     args = [send_mail, logged_user_email]
    #     main_threading(args, sender.batch_email_users)

    #     return Response(
    #         {"message": "File already uploaded"},
    #         status=status.HTTP_201_CREATED
    #     )



    @action(detail=True,
            methods=['put'],
            url_path='reset-password',
            name="Reset Password of User")
    @decorators.error_safe
    @rms.reset_password
    @transaction.atomic
    def ResetPassword(self, request, code=None):

        existingUser = User.objects.filter(code=str(code))
        pk = existingUser.values().first()['id']
        existingUser = existingUser.first()

        if existingUser:

            password = User.objects.make_random_password(length=10)
            existingUser.set_password(password)

            fromObj = copy.copy(existingUser)
            existingUser.save()
            toObj = copy.copy(existingUser)

            log_save(
                enums.LogEnum.UPDATE.value,
                enums.LogEntitiesEnum.USER.value,
                pk,
                model_to_dict(fromObj),
                model_to_dict(toObj))

            name = existingUser.name
            username = existingUser.username
            account_email = existingUser.email
            admin_email = request.user.email

            args = [name, username, password, account_email, admin_email]

            main_threading(args, sender.admin_changepassword)

        else:

            raise Exception('User not found')

        return Response(
            {"detail": "Success"},
            status=status.HTTP_200_OK
        )

    @action(detail=True,
            methods=['put'],
            url_path='change-password',
            name="Change Password of User")
    # @decorators.error_safe
    @transaction.atomic
    def ChangePassword(self, request, code=None):

        self.serializer_class = serializer.ChangePasswordSerializer
        serialized = self.get_serializer(data=request.data, context={'id': code})

        if serialized.is_valid():

            # form = copy.deepcopy(serialized.validated_data)
            form = request.data

            if form['new_password'] != form['new_password_confirm']:
                raise Exception('Passwords must match')

            existingUser = User.objects.filter(code=code)
            pk = existingUser.values().first()['id']


            if existingUser:

                existingUser.first().set_password(form['new_password_confirm'])
                fromObj = copy.copy(existingUser.first())
                existingUser.first().save()
                toObj = copy.copy(existingUser.first())

                log_save(
                    enums.LogEnum.UPDATE.value,
                    enums.LogEntitiesEnum.USER.value,
                    pk,
                    model_to_dict(fromObj),
                    model_to_dict(toObj)
                )

                return Response(
                    {"message": "Password successfully changed"},
                    status=status.HTTP_200_OK
                )
            else:
                raise Exception('User not found')
        else:
            serialized.is_valid(raise_exception=True)

        return Response(
            data={"detail": "Error"},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR
        )


    @action(detail=True,
            methods=['put'],
            url_path='edit-profile',
            name="Edit User")
    @transaction.atomic
    def EditProfile(self, request, code=None):

        self.serializer_class = serializer.UserEditSerializer
        serialized = self.get_serializer(data=request.data)

        if serialized.is_valid():

            # form = copy.deepcopy(serialized.validated_data)
            # form = request.data

            # if form['new_password'] != form['new_password_confirm']:
            #     raise Exception('Passwords must match')

            existingUser = User.objects.filter(code=code)
            pk = self.get_object()

            if existingUser:

                # existingUser.first().set_password(form['new_password_confirm'])
                fromObj = copy.copy(existingUser.first())
                existingUser.first().save()
                toObj = copy.copy(existingUser.first())

                log_save(
                    enums.LogEnum.UPDATE.value,
                    enums.LogEntitiesEnum.USER.value,
                    int(pk.id),
                    model_to_dict(fromObj),
                    model_to_dict(toObj)
                )

                return Response(
                    {"message": "User successfully edit"},
                    status=status.HTTP_200_OK
                )
            else:
                raise Exception('User not found')
        else:
            serialized.is_valid(raise_exception=True)

        return Response(
            data={"detail": "Error"},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR
        )


    @action(detail=True,
            methods=['put'],
            url_path='picture',
            name="Uploads Profile Picture of User")
    # @decorators.error_safe
    @transaction.atomic
    def UploadProfilePicture(self, request, code=None):
        existingUser = User.objects.filter(code=code).first()
        if existingUser:

            eximages = UserImage.objects.filter(user_id=code)

            if (eximages):
                for item in eximages:
                    item.delete()
                    # DELETE FROM PHYSICAL
                    FileHelper.DeleteFile(path=item.image.path)

            # self.serializer_class = serializer.UserImageSerializer

            data = serializer.UserImageSerializer(data=request.data)
            data.is_valid(raise_exception=True)
            self.perform_create(data)
            headers = self.get_success_headers(data.data)

            return Response(data.data,
                            status=status.HTTP_201_CREATED,
                            headers=headers)
        else:
            raise ParseError('User not found')
        return Response(data={"detail": "Success"})
