import os
import datetime
import threading
import configparser
import pandas as pd
from datetime import datetime
from app.entities import enums
from django.db import transaction
from app.helper import decorators
from django.db import IntegrityError
from app.helper.email_service import sender
from rest_framework import status, viewsets
from rest_framework.response import Response
from app.applicationlayer.management.batchupload.serializer import (
    ExtractTransformLoadSerializer,
    UserHistorySerializer,
    BatchUploadSerializer
)
from app.entities.models import (
    User, Application, ExtractTransformLoad,
    AllowedCompany, Company, Department, UserHistory, MasterAttachment
)
from app.applicationlayer.utils import (
    status_message_response,
    main_threading, PaginationForETL
)
from django.db.models import Q
from rest_framework.exceptions import ParseError
from rest_framework.decorators import action
from django.contrib.auth.hashers import make_password
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter, OrderingFilter
from app.applicationlayer.management.batchupload.table_filters import ExtractTransformLoadFilter
from django.db.models import F, Value
from django.core.files.base import ContentFile

config = configparser.ConfigParser()
config_file = os.path.join('./', 'env.ini')
config.read(config_file)
from django.conf import settings
from io import BytesIO
from django.http import HttpResponse
from django.template.loader import get_template
from xhtml2pdf import pisa
from django.core.files.storage import FileSystemStorage
from django.core.exceptions import ObjectDoesNotExist
import sys
import os
import zipfile
import shutil

from reportlab.platypus import SimpleDocTemplate, Spacer, Paragraph
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.pagesizes import A4
from reportlab.lib.enums import TA_JUSTIFY, TA_RIGHT, TA_LEFT, TA_CENTER
from reportlab.lib.colors import red

class BatchUploadViewSet(viewsets.ModelViewSet):
    queryset = ExtractTransformLoad.objects.all().order_by('-created')
    serializer_class = ExtractTransformLoadSerializer
    pagination_class = PaginationForETL
    lookup_field = 'code'
    filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
    filterset_class = ExtractTransformLoadFilter
    ordering_fields = (
        'file_name', 'model_type', 'code'
    )
    search_fields = (
        'file_name', 'model_type', 'code'
    )

    def list(self, request, *args, **kwargs):
        logged_user = request.user.username
        queryset = self.filter_queryset(self.get_queryset().filter(createdby=logged_user))
        

        page = self.paginate_queryset(queryset)

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

            message = status_message_response(
                200,
                'success',
                '',
                serializer.data
            )

            return self.get_paginated_response(message)

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

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

    @action(
        detail=True, methods=['get'],
        url_path='users', name='list of batch upload users'
    )
    def Users(self, request, code=None):
        self.serializer_class = UserHistorySerializer
        queryset = UserHistory.objects.filter(file_name=code)
        page = self.paginate_queryset(queryset)

        serializer = self.get_serializer(page, many=True)
        message = status_message_response(
            200,
            'success',
            'List of User found',
            serializer.data
        )

        return self.get_paginated_response(message)

    @action(
        detail=False, methods=['get'],
        url_path='pdf-instruction', name='how to upload bulk users'
    )
    def testpdf(self, request, code=None):
        F = open(os.path.join(settings.EMAIL_TEMPLATES_ROOT, 'RMS-NEWUSER.html'), 'r')
        result = BytesIO()
        pdf = pisa.pisaDocument(F, result)
        updated_file = ContentFile(result.getvalue())
        updated_file.name = "cr_download.pdf"
        
        my_folder = os.path.join(settings.MEDIA_ROOT, f'cr/{request.user.code}')
        if os.path.isdir(my_folder):
            shutil.rmtree(my_folder)
        
        user_folder = os.mkdir(my_folder)
        completeName = os.path.join(os.path.join(settings.MEDIA_ROOT, f'cr/{request.user.code}'), updated_file.name)         

        file1 = open(completeName, "w+")

        # response = HttpResponse(open(my_folder, 'rb'), content_type='application/zip')
        # response['Content-Disposition'] = 'attachment; filename=any_name_you_like.zip'
        # return response

        message = status_message_response(
            200,
            'success',
            'test',
            "sss"
        )
        return Response(message)
        # return sender.to_pdf()

    @action(
        detail=False, methods=['get'],
        url_path='user-instruction', name='how to upload bulk users'
    )
    def UserInstruction(self, request, code=None):
        attach_instruc = MasterAttachment.objects.filter(attch_ref='etl_instruction')
        if attach_instruc:
            print("instruction to be deleted?")
            attach_instruc.first().delete()
            print("instuction deleted!")

        outfilename = "instruction.pdf"
        outfiledir = 'media/uploads/'
        outfilepath = os.path.join(outfiledir, outfilename)
        
        doc = SimpleDocTemplate(outfilepath,
                                pagesize=A4,
                                rightMargin=72,
                                leftMargin=72,
                                topMargin=72,
                                bottomMargin=18)
        
        instruction_data = []

        styles = getSampleStyleSheet()

        styles.add(ParagraphStyle(name='Justify',
                                  alignment=TA_JUSTIFY,
                                  fontName='Helvetica',
                                  fontSize=12))
        styles.add(ParagraphStyle('reminder',
                                  fontName='Helvetica',
                                  fontSize=13,
                                  leading=28,
                                  textColor=red))

        items = [
            "1. To be able to add multiple users in the system, you must first <u>Download the</u>",
            "<font color=white>....</font><u>template.</u>",
            "2. This file includes a sample entry as your basis for adding users in the file",
            "3. This file also includes all the Departments currently in the system, as your basis",
            "<font color=white>....</font>in indicating privileges for each user. ",
            "4. Make sure to indicate the Department Code, Department Name and Company",
            "<font color=white>....</font>Name (Department details) that you would want the user to access.",
            "5. If the user should have access to multiple Departments, just repeat all the details",
            "<font color=white>...</font>of the user and change the Department details and privileges assignment in each",
            "<font color=white>...</font>line for the user.",
            "6. Take note of the following Application Abbreviations, to be indicated in the",
            "<font color=white>...</font>Application and Default Application (default_app) column of the CSV file: ",
            "<font color=white>...</font>6.1.<b>RMS</b> is Resource Management System",
            "<font color=white>...</font>6.2.<b>CMS</b> is Change Request Management System",
            "<font color=white>...</font>6.3.<b>AMS</b> is Asset Management System",
            "7. The template is in CSV format, after filling up the details, make sure that the file ",
            "<font color=white>...</font>is still saved in CSV format before uploading in the system."
        ]
        
        ptext = '<font size=13>IMPORTANT REMINDERS</font>'
        instruction_data.append(Paragraph(ptext, styles["reminder"]))
        instruction_data.append(Spacer(1, 15))

        for item in items:
            ptext = '<font size=12>' + item + '</font>'
            instruction_data.append(Paragraph(ptext, styles["Justify"]))
            instruction_data.append(Spacer(1, 12))

        doc.build(instruction_data)

        uploaded = MasterAttachment.objects.create(
            url='uploads/instruction.pdf',
            attch_ref='etl_instruction'
        )
        self.serializer_class = BatchUploadSerializer
        queryset = MasterAttachment.objects.filter(id=uploaded.id)
        page = self.paginate_queryset(queryset)

        serializer = self.get_serializer(page, many=True)
        message = status_message_response(
            200,
            'success',
            'User Upload Instruction is found',
            serializer.data
        )
        return self.get_paginated_response(message)


    @action(
        detail=False, methods=['get'],
        url_path='user-format-download', name='Users file format'
    )
    def UserFormat(self, request, code=None):
        attach_user = MasterAttachment.objects.filter(attch_ref='etl_gen')
        if attach_user:
            attach_user.first().delete()

        departments = Department.objects.exclude(id=1).annotate(
            Privilege_Company=F('company__name'),
            Privilege_Department=F('name'),
            Privilege_Department_Code=F('code')
        ).values(
            'Privilege_Company',
            'Privilege_Department',
            'Privilege_Department_Code'
        )
        
        data = {
            'Username': ["ob-john"],
            'Name': ["John Doe"],
            'Company': [departments[0]['Privilege_Company']],
            'Department': [departments[0]['Privilege_Department']],
            'Department_Code': [departments[0]['Privilege_Department_Code']],
            'Email': ["johndoe@gmail.com"],
            'Contact_No': ["123456"],
            'Application': ["cms,ams"],
            'Default_app': ["cms"],
            'Privilege_Company': [departments[0]['Privilege_Company']],
            'Privilege_Department': [departments[0]['Privilege_Department']],
            'Privilege_Department_Code': [departments[0]['Privilege_Department_Code']],
            'Create_CR': ["Yes"],
            'Create_Template': ["No"],
            'View_All_CR': ["Yes"],
            'Approve_CR': ["Yes"]
        }

        cols = ['Username', 'Name', 'Company', 'Department', 'Department_Code']
        cols += ['Email', 'Contact_No', 'Application', 'Default_app']
        cols += ['Privilege_Company', 'Privilege_Department', 'Privilege_Department_Code']
        cols += ['Create_CR', 'Create_Template', 'View_All_CR', 'Approve_CR']

        df = pd.DataFrame(data)

        header = {"Privilege_Company": ['Please use this department code as reference']}
        df3 = pd.DataFrame(
            header
        )
        df2 = pd.DataFrame(
            departments
        )
        df_row_reindex = pd.concat([df, df3, df2])
        df_row_reindex = df_row_reindex.reindex(columns=cols)
        df_row_reindex = df_row_reindex.to_csv(index=False)
        updated_file = ContentFile(df_row_reindex)
        updated_file.name = "user_format.csv"


        uploaded = MasterAttachment.objects.create(
            url=updated_file,
            attch_ref='etl_gen'
        )
        self.serializer_class = BatchUploadSerializer
        queryset = MasterAttachment.objects.filter(id=uploaded.id)
        page = self.paginate_queryset(queryset)

        serializer = self.get_serializer(page, many=True)
        message = status_message_response(
            200,
            'success',
            'User File format is found',
            serializer.data
        )
        return self.get_paginated_response(message)

    @transaction.atomic
    def create(self, request, **kwargs):
        csv_file = request.FILES['file']
        extension_file = str(csv_file).split('.')[1]
        if extension_file.lower() != 'csv':
            return Response(
                {"message": "Only csv extension file is allowed"},
                status=status.HTTP_400_BAD_REQUEST
            )

        df = pd.read_csv(csv_file, sep=',', skip_blank_lines=True).dropna()
        # df.dropna()
        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 = []

        department_error = 'Department should be same with the logged user'
        company_error = 'Company should be same with the logged user'

        etl = ExtractTransformLoad.objects.create(
            file_name=str(csv_file),
            model_type=enums.GenerateCode.USER.value
        )
        etl2 = ExtractTransformLoad.objects.get(id=etl.id)

        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

        for data, keys in df.iterrows():
            try:
                default_app = Application.objects.filter(
                    excel_code=keys['Default_app']
                ).first()

                # user type authorization and user type per user

                if request.user.user_type == enums_company:
                    user_company = request.user.department.company.code
                    request_department = Department.objects.filter(
                        Q(code=keys['Department_Code']) &
                        Q(company=user_company)
                    )
                    if not request_department:
                        # raise ParseError(company_error)
                        return Response(
                            {"message": company_error},
                            # {"message": str(e)},
                            status=status.HTTP_400_BAD_REQUEST
                        )

                elif request.user.user_type == enums_department:
                    if keys['Department_Code'] != request.user.department.code:
                        return Response(
                            {"message": department_error},
                            # {"message": str(e)},
                            status=status.HTTP_400_BAD_REQUEST
                        )
                        # raise ParseError(
                        #     department_error
                        # )

                # user type authorization and user type per user
                else:
                    user_department = Department.objects.filter(
                        name__icontains=keys['Department_Code']
                    )
                
                default_app = Application.objects.filter(
                    excel_code=keys['Default_app']
                ).first()

                dept_code = Department.objects.get(
                    code=keys['Department_Code']
                )
                
                users = {
                    "username": keys['Username'],
                    "name": keys['Name'],
                    "department": dept_code,
                    "email": keys['Email'],
                    "contact_no": keys['Contact_No'],
                    "default_app": default_app,
                    "user_type": enums_user
                }

                check_user = User.objects.filter(username=keys['Username'])

                if not check_user:

                    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(',')
                    )

                    current_user.application.set(app)
                    current_user = User.objects.get(id=current_user.id)

                    del users['department']
                    del users['contact_no']
                    del users['default_app']
                    del users['user_type']
                    users['file_name'] = etl2

                    users['password'] = password
                    # users['admin'] = logged_user_email

                    UserHistory.objects.create(
                        **users
                    )

                elif check_user.count() > 0 and not check_user[0].date_joined.strftime('%Y-%m-%d') == datetime.now().strftime('%Y-%m-%d'):
                    # i insert it on database to make an integrity error it means this row is already exist
                    current_user = User.objects.create(
                        **users
                    )
                else:
                    current_user = check_user[0]

                try:
                    privilege_department_code = keys['Privilege_Department_Code']
                    privilege_department_code = Department.objects.get(
                        code=privilege_department_code
                    )

                    ccr = True if keys['Create_CR'].lower() == 'yes' else False
                    crt = True if keys['Create_Template'].lower() == 'yes' else False
                    view_all = True if keys['View_All_CR'].lower() == 'yes' else False
                    approve_cr = True if keys['Approve_CR'].lower() == 'yes' else False

                    privilege_object = {
                        "id_number": current_user,
                        "company_pivot": privilege_department_code.company,
                        "group_pivots": privilege_department_code,
                        "create_change_request": ccr,
                        "create_change_request_template": crt,
                        "view_all_change_request": view_all,
                        "approve_cr": approve_cr
                    }

                    AllowedCompany.objects.create(**privilege_object)

                except IntegrityError as e:
                    return Response(
                        {"message": f"Duplicate user privilege at row {data + 2}"},
                        # {"message": str(e)},
                        status=status.HTTP_400_BAD_REQUEST
                    )

                except ObjectDoesNotExist as e:
                    return Response(
                        {"message": f"Department Does not Exist at Privilege row {data + 2}"},
                        status=status.HTTP_400_BAD_REQUEST
                    )

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

            except KeyError as e:
                return Response(
                    {"message": f"Missing column {e.args[0]}"},
                    status=status.HTTP_400_BAD_REQUEST
                )
            
            except ObjectDoesNotExist as e:
                return Response(
                    {"message": f"Department Does not Exist at row {data + 2}"},
                    status=status.HTTP_400_BAD_REQUEST
                )

            # except ValueError as e:
            #     return Response(
            #         {"message": f"Invalid department code. Please check at line {data + 2}"},
            #         status=status.HTTP_400_BAD_REQUEST
            #     )

        send_mail = UserHistory.objects.filter(sent=False).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, etl.code]
        main_threading(args, sender.batch_email_users)

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