from django.db import models
from django.contrib.auth.models import AbstractUser
from .modelcommon import AuditClass
from .UserManager import UserManager
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver
from datetime import datetime
from . import enums
from django.utils.text import slugify


# ********************* AUTH TABLES *********************


def number_generator(prefix, id):
    date = '{:%Y%m%d}'.format(datetime.now())
    id_num = '{:07}'.format(id)
    autogenerated_no = prefix + '-' + date + '-' + id_num

    return autogenerated_no


class Application(AuditClass):
    code = models.CharField(
        unique=True,
        max_length=255
    )
    name = models.CharField(unique=True, max_length=255)

    class Meta:
        db_table = 'applications'

    def __str__(self):
        return f'{self.name}'

    def save(self, *args, **kwargs):
        super(Application, self).save(*args, **kwargs)
        code = enums.GenerateCode.APP.value
        code = number_generator(enums.GenerateCode.APP.value, self.id)
        Application.objects.filter(id=self.id).update(code=code)


class Module(AuditClass):
    application = models.ForeignKey(
        Application,
        related_name='modules',
        on_delete=models.PROTECT
    )
    code = models.CharField(
        unique=True,
        max_length=255,
        default='mod'
    )
    name = models.CharField(
        unique=True,
        max_length=255,
        blank=True,
        null=True
    )
    parent = models.IntegerField()
    sort_id = models.IntegerField()
    component = models.CharField(
        max_length=255,
        null=True,
        blank=True)

    class Meta:
        db_table = 'modules'

    def __str__(self):
        return f'{self.name}'

    def save(self, *args, **kwargs):
        super(Module, self).save(*args, **kwargs)
        code = enums.GenerateCode.APP.value
        code = number_generator(enums.GenerateCode.MODULE.value, self.id)
        Module.objects.filter(id=self.id).update(code=code)


class Company(AuditClass):
    code = models.CharField(
        unique=True,
        max_length=255,
        default='com'
    )
    name = models.CharField(unique=True, max_length=255)
    contact_details = models.TextField(blank=True, null=True)

    class Meta:
        db_table = 'companies'

    def __str__(self):
        return f'{self.name}'

    def save(self, *args, **kwargs):
        super(Company, self).save(*args, **kwargs)
        code = enums.GenerateCode.APP.value
        code = number_generator(enums.GenerateCode.COMPANY.value, self.id)
        Company.objects.filter(id=self.id).update(code=code)


class Department(AuditClass):
    # parent = models.ForeignKey(
    #     'Department',
    #     related_name='departments_parent',
    #     on_delete=models.PROTECT,
    #     blank=True, null=True
    # )
    company = models.ForeignKey(
        Company,
        related_name='companies',
        on_delete=models.PROTECT
    )
    code = models.CharField(
        unique=True,
        max_length=255,
        default='dept'
    )
    name = models.CharField(unique=True, max_length=255)

    class Meta:
        db_table = 'departments'

    def __str__(self):
        return f'{self.name}'

    def save(self, *args, **kwargs):
        super(Department, self).save(*args, **kwargs)
        code = enums.GenerateCode.APP.value
        code = number_generator(enums.GenerateCode.DEPARTMENT.value, self.id)
        Department.objects.filter(id=self.id).update(code=code)


# class Position(AuditClass):
#     parent = models.ForeignKey('Position',
#                                related_name='position_subs',
#                                on_delete=models.PROTECT,
#                                blank=True, null=True)
#     code = models.CharField(unique=True, max_length=255)
#     name = models.CharField(unique=True, max_length=255)
#     description = models.CharField(max_length=255, blank=True, null=True)

#     department = models.ForeignKey(Department,
#                                    related_name='positions',
#                                    on_delete=models.PROTECT)

#     class Meta:
#         db_table = 'positions'


class Role(AuditClass):
    description = models.CharField(
        max_length=255,
        blank=True,
        null=True
    )
    code = models.CharField(
        unique=True,
        max_length=255,
        default='role'
    )
    name = models.CharField(max_length=255)

    class Meta:
        db_table = 'roles'


class Permission(AuditClass):
    name = models.CharField(max_length=255, blank=False, null=True)
    code = models.CharField(
        unique=True,
        max_length=255,
        blank=True,
        null=True
    )
    description = models.CharField(
        max_length=255,
        blank=False,
        null=True
    )

    class Meta:
        db_table = 'permissions'


class RolePermission(AuditClass):
    role = models.ForeignKey(
        Role,
        related_name='role_permissions',
        on_delete=models.PROTECT
    )
    permission = models.ForeignKey(
        Permission,
        related_name='role_permissions',
        on_delete=models.PROTECT
    )

    class Meta:
        db_table = 'role_permissions'


class User(AbstractUser):
    application = models.ManyToManyField(
        Application
    )
    department = models.ForeignKey(
        Department, related_name='department_users',
        on_delete=models.PROTECT,
        default=1
    )
    # role = models.ForeignKey(
    #     Role,
    #     related_name='role_users',
    #     on_delete=models.PROTECT,
    #     default=1
    # )
    user_type = models.CharField(
        choices=[(tag.value, tag.value) for tag in enums.UserTypeEnum],
        default=enums.UserTypeEnum.USER.value,
        max_length=100
    )
    code = models.CharField(
        unique=True,
        max_length=255,
        default='acct'
    )
    name = models.CharField(max_length=255)
    username = models.CharField(max_length=255, unique=True)
    password = models.CharField(
        max_length=255,
        blank=True,
        null=True
    )
    doa = models.ForeignKey(
        'User', related_name='doa_users',
        on_delete=models.PROTECT,
        blank=True,
        null=True
    )
    contact_no = models.CharField(blank=True, null=True, max_length=255)
    email = models.EmailField(max_length=255, unique=False)
    objects = UserManager()

    class Meta:
        db_table = 'auth_user'

    def __str__(self):
        return f'{self.name}'

    def save(self, *args, **kwargs):
        super(User, self).save(*args, **kwargs)
        code = enums.GenerateCode.APP.value
        code = number_generator(enums.GenerateCode.USER.value, self.id)
        User.objects.filter(id=self.id).update(code=code)


class UserImage(AuditClass):
    user = models.ForeignKey(
        User,
        related_name='user_images',
        on_delete=models.PROTECT
    )
    name = models.CharField(blank=True, null=True, max_length=255)
    image = models.ImageField(upload_to='user_images/', null=True,
                              max_length=255)
    is_primary = models.BooleanField(default=True)

    class Meta:
        db_table = 'auth_user_images'


class Attachment(AuditClass):
    code = models.FileField(
        upload_to='file_attachments/',
        null=True,
        max_length=255
    )
    name = models.CharField(unique=True, max_length=255)
    description = models.CharField(
        max_length=255,
        blank=True,
        null=True
    )

    class Meta:
        db_table = 'attachments'


"""
**********************
*** UTILITY TABLES ***
**********************
"""


class AuthToken(models.Model):
    ref = models.CharField(max_length=255)
    token = models.TextField()
    passcode = models.CharField(max_length=255)
    timeout = models.IntegerField()
    is_active = models.BooleanField(default=False)
    user = models.ForeignKey(User,
                             related_name='auth_access_token',
                             on_delete=models.PROTECT)

    class Meta:
        db_table = 'auth_access_token'


class EntityLog(AuditClass):
    action = models.CharField(
        choices=[
            (tag.value, tag.value) for tag in enums.LogEnum],
        default=enums.LogEnum.ADD.value,
        max_length=50)
    entity = models.CharField(
        choices=[
            (tag.value, tag.value) for tag in enums.LogEntitiesEnum],
        default=enums.LogEnum.ADD.value,
        max_length=50)
    row_id = models.IntegerField()
    fromValue = models.TextField(blank=True, null=True)
    toValue = models.TextField(blank=True, null=True)

    def __str__(self):
        return self.entity

    class Meta:
        db_table = 'entity_logs'


"""
**********************
*** CHANGE REQUEST TABLES ***
**********************
"""

class BaseHeader(models.Model):

    requested_to_company = models.ForeignKey(
        Company,
        on_delete=models.DO_NOTHING,
        to_field='code')

    requested_to_department = models.ForeignKey(
        Department,
        on_delete=models.DO_NOTHING,
        to_field='code')

    requested_to_user = models.ForeignKey(
        User,
        on_delete=models.DO_NOTHING,
        to_field='code')

    requested_to_template_name = models.CharField(max_length=255)
    requested_to_template_id = models.CharField(max_length=255)

    requested_to_objective = models.CharField(
        max_length=255,
        blank=True,
        null=True)

    requested_to_target_date = models.DateTimeField(
        blank=True,
        null=True)

    requested_to_priority = models.CharField(max_length=255)

    description = models.CharField(
        max_length=255,
        blank=True,
        null=True)

    created = models.DateTimeField(auto_now_add=True)

    deleted_at = models.DateTimeField(
        blank=True,
        null=True)

    class Meta:
        abstract = True


class BaseApprover(models.Model):
    level = models.CharField(
        max_length=50,
        blank=True,
        null=True)

    user = models.ForeignKey(
        User,
        blank=True,
        null=True,
        on_delete=models.DO_NOTHING,
        to_field='code')

    delegation = models.CharField(max_length=50, blank=True, null=True)
    created = models.DateTimeField(
        blank=True,
        null=True)
    deleted_at = models.DateTimeField(
        blank=True,
        null=True)

    class Meta:
        abstract = True


class BaseStakeholder(models.Model):
    user = models.ForeignKey(
        User,
        blank=True,
        null=True,
        on_delete=models.DO_NOTHING,
        to_field='code')
    delegation = models.CharField(
        max_length=255,
        blank=True,
        null=True)
    created = models.DateTimeField(
        blank=True,
        null=True)
    deleted_at = models.DateTimeField(
        blank=True,
        null=True)

    class Meta:
        abstract = True


class BaseAttachment(models.Model):
    attachment_type = models.CharField(max_length=255)
    attachment_name = models.CharField(max_length=255)

    file_name = models.CharField(
        max_length=255,
        blank=True,
        null=True)
    description = models.CharField(
        max_length=255,
        blank=True,
        null=True)
    uploaded_by = models.ForeignKey(
        User,
        on_delete=models.DO_NOTHING,
        to_field='code')
    file_upload = models.FileField(
        upload_to='uploads/',
        blank=True,
        null=True)
    created = models.DateTimeField(
        blank=True,
        null=True)
    deleted_at = models.DateTimeField(
        blank=True,
        null=True)

    class Meta:
        abstract = True


class BaseDetails(models.Model):
    field_idx = models.TextField(max_length=255)
    field_ref = models.TextField(max_length=255)
    field_val = models.TextField(max_length=255)
    field_props = models.TextField(max_length=255)

    created = models.DateTimeField(
        blank=True,
        null=True)
    deleted_at = models.DateTimeField(
        blank=True,
        null=True)

    class Meta:
        abstract = True


'''
*********
TEMPLATES
*********
'''

class ChangeRequestTemplateHeader(BaseHeader):
    template_no = models.CharField(
        unique=True,
        max_length=255)

    created_by_user = models.ForeignKey(
        User,
        on_delete=models.DO_NOTHING,
        to_field='code',
        related_name='created_by_user')

    created_by_department = models.ForeignKey(
        Department,
        on_delete=models.DO_NOTHING,
        to_field='code',
        related_name='created_by_department')

    class Meta:
        db_table = 'change_request_template_headers'

    def __str__(self):
        return f'{self.requested_to_template_name}'

    def save(self, *args, **kwargs):
        super(ChangeRequestTemplateHeader, self).save(*args, **kwargs)
        code = number_generator(enums.GenerateCode.TEMPLATE.value, self.id)
        # ChangeRequestTemplateHeader.objects.filter(id=self.id).update(template_no=code)

        if self.template_no == '':
            self.template_no = code
            self.save()

    def delete(self):
        self.deleted_at = datetime.utcnow
        self.save()


class ChangeRequestTemplateApprovers(BaseApprover):
    code = models.CharField(
        unique=True,
        max_length=255)  # primary key
    template_no = models.ForeignKey(
        ChangeRequestTemplateHeader,
        on_delete=models.DO_NOTHING,
        to_field='template_no',
        related_name='tmp_approvers')

    class Meta:
        db_table = 'change_request_template_approvers'

    def save(self, *args, **kwargs):
        super(ChangeRequestTemplateApprovers, self).save(*args, **kwargs)
        code = number_generator(enums.GenerateCode.TMP_APPROVER.value, self.id)
        # ChangeRequestTemplateApprovers.objects.filter(id=self.id).update(code=code)

        if self.code == '':
            self.code = code
            self.created = datetime.now()
            self.save()

class ChangeRequestTemplateStakeHolders(BaseStakeholder):
    code = models.CharField(
        unique=True,
        max_length=255)  # primary key
    template_no = models.ForeignKey(
        ChangeRequestTemplateHeader,
        on_delete=models.DO_NOTHING,
        to_field='template_no',
        related_name='tmp_stakes')
    
    class Meta:
        db_table = 'change_request_template_stakeholders'
    
    def save(self, *args, **kwargs):
        super(ChangeRequestTemplateStakeHolders, self).save(*args, **kwargs)
        code = number_generator(enums.GenerateCode.TMP_STAKE.value, self.id)
        # ChangeRequestTemplateStakeHolders.objects.filter(id=self.id).update(code=code)

        if self.code == '':
            self.code = code
            self.created = datetime.now()
            self.save()


class ChangeRequestTemplateAttachments(BaseAttachment):
    code = models.CharField(
        max_length=255,
        unique=True)
    template_no = models.ForeignKey(
        ChangeRequestTemplateHeader,
        on_delete=models.DO_NOTHING,
        to_field='template_no',
        related_name='tmp_attachments')

    class Meta:
        db_table = 'change_request_template_attachments'
    
    def save(self, *args, **kwargs):
        super(ChangeRequestTemplateAttachments, self).save(*args, **kwargs)
        code = number_generator(enums.GenerateCode.TMP_ATTACH.value, self.id)
        # ChangeRequestTemplateAttachments.objects.filter(id=self.id).update(attachment_no=code)

        if self.code == '':
            self.code = code
            self.created = datetime.now()
            self.save()


class ChangeRequestTemplateDetails(BaseDetails):
    code = models.CharField(
        max_length=255,
        unique=True)
    template_no = models.ForeignKey(
        ChangeRequestTemplateHeader,
        on_delete=models.DO_NOTHING,
        to_field='template_no',
        related_name='tmp_details')

    class Meta:
        db_table = 'change_request_template_details'

    def save(self, *args, **kwargs):
        super(ChangeRequestTemplateDetails, self).save(*args, **kwargs)
        code = number_generator(enums.GenerateCode.TMP_DETAIL.value, self.id)
        # ChangeRequestTemplateDetails.objects.filter(id=self.id).update(code=code)

        if self.code == '':
            self.code = code
            self.created = datetime.now()
            self.save()

'''
*****
FORMS
*****
'''

class ChangeRequestFormHeader(BaseHeader):
    form_code = models.CharField(
        max_length=255,
        unique=True)
    requested_by_user = models.ForeignKey(
        User,
        on_delete=models.DO_NOTHING,
        to_field='code',
        related_name='requested_by_user')
    cancel_date = models.DateTimeField(
        null=True,
        blank=True)
    requested_by_department = models.ForeignKey(
        Department,
        on_delete=models.DO_NOTHING,
        to_field='code',
        related_name='requested_by_department')
    template_no = models.ForeignKey(
        ChangeRequestTemplateHeader,
        on_delete=models.DO_NOTHING,
        to_field='template_no')
    status = models.CharField(max_length=50)
    
    company_desc = models.CharField(
        max_length=255,
        null=True,
        blank=True)
    department_desc = models.CharField(
        max_length=255,
        null=True,
        blank=True)
    requested_desc = models.CharField(
        max_length=255,
        null=True,
        blank=True)
    old_form_code = models.CharField(
        max_length=255,
        null=True,
        blank=True)

    class Meta:
        db_table = 'change_request_form_headers'
    
    def save(self, *args, **kwargs):
        super(ChangeRequestFormHeader, self).save(*args, **kwargs)
        code = number_generator(enums.GenerateCode.FORM.value, self.id)
        # ChangeRequestFormHeader.objects.filter(id=self.id).update(form_code=code)
    
        if self.form_code == '':
            self.form_code = code
            self.save()


class ChangeRequestFormApprovers(BaseApprover):
    code = models.CharField(
        unique=True,
        max_length=255)  # primary key
    form_code = models.ForeignKey(
        ChangeRequestFormHeader,
        on_delete=models.DO_NOTHING,
        to_field='form_code',
        related_name='frm_approvers')
    remarks = models.CharField(
        max_length=255,
        null=True,
        blank=True)
    action = models.CharField(
        max_length=50,
        blank=True,
        null=True)
    date_sent = models.DateTimeField(
        blank=True,
        null=True)
    tmp_approver = models.ForeignKey(
        ChangeRequestTemplateApprovers,
        null=True,
        blank=True,
        on_delete=models.DO_NOTHING,
        to_field='code')

    class Meta:
        db_table = 'change_request_form_approvers'

    def save(self, *args, **kwargs):
        super(ChangeRequestFormApprovers, self).save(*args, **kwargs)
        code = number_generator(enums.GenerateCode.FORM_APPROVER.value, self.id)
        # ChangeRequestFormApprovers.objects.filter(id=self.id).update(code=code)

        if self.code == '':
            self.code = code
            self.created = datetime.now()
            self.save()


class ChangeRequestFormDetails(BaseDetails):
    code = models.CharField(
        unique=True,
        max_length=255)  # primary key
    form_code = models.ForeignKey(
        ChangeRequestFormHeader,
        on_delete=models.DO_NOTHING,
        to_field='form_code',
        related_name='frm_details')
    tmp_detail = models.ForeignKey(
        ChangeRequestTemplateDetails,
        null=True,
        blank=True,
        on_delete=models.DO_NOTHING,
        to_field='code')

    class Meta:
        db_table = 'change_request_form_details'
    
    def save(self, *args, **kwargs):
        super(ChangeRequestFormDetails, self).save(*args, **kwargs)
        code = number_generator(enums.GenerateCode.FORM_DETAIL.value, self.id)
        # ChangeRequestFormDetails.objects.filter(id=self.id).update(code=code)

        if self.code == '':
            self.code = code
            self.created = datetime.now()
            self.save()


class ChangeRequestFormStakeHolders(BaseStakeholder):
    code = models.CharField(
        unique=True,
        max_length=255)  # primary key
    form_code = models.ForeignKey(
        ChangeRequestFormHeader,
        on_delete=models.DO_NOTHING,
        to_field='form_code',
        related_name='frm_stakes')
    date_added = models.DateTimeField(
        blank=True,
        null=True)
    tmp_stake = models.ForeignKey(
        ChangeRequestTemplateStakeHolders,
        null=True,
        blank=True,
        on_delete=models.DO_NOTHING,
        to_field='code')

    class Meta:
        db_table = 'change_request_form_stakeholders'

    def save(self, *args, **kwargs):
        super(ChangeRequestFormStakeHolders, self).save(*args, **kwargs)
        code = number_generator(enums.GenerateCode.FORM_STAKE.value, self.id)
        # ChangeRequestFormStakeHolders.objects.filter(id=self.id).update(code=code)

        if self.code == '':
            self.code = code
            self.created = datetime.now()
            self.save()


class ChangeRequestFormAttachments(BaseAttachment):
    code = models.CharField(
        unique=True,
        max_length=255)  # primary key
    form_code = models.ForeignKey(
        ChangeRequestFormHeader,
        on_delete=models.DO_NOTHING,
        to_field='form_code',
        related_name='frm_attachments')
    tmp_attach = models.ForeignKey(
        ChangeRequestTemplateAttachments,
        null=True,
        blank=True,
        on_delete=models.DO_NOTHING,
        to_field='code')

    class Meta:
        db_table = 'change_request_form_attachments'

    def save(self, *args, **kwargs):
        super(ChangeRequestFormAttachments, self).save(*args, **kwargs)
        code = number_generator(enums.GenerateCode.FORM_ATTACH.value, self.id)
        # ChangeRequestFormAttachments.objects.filter(id=self.id).update(code=code)
    
        if self.code == '':
            self.code = code
            self.created = datetime.now()
            self.save()

'''
*****
CR HISTORY
*****
'''

class ChangeRequestHistory(AuditClass):
    action = models.CharField(
        choices=[
            (tag.value, tag.value) for tag in enums.LogEnum],
        default=enums.LogEnum.ADD.value,
        max_length=50)
    entity = models.CharField(
        choices=[
            (tag.value, tag.value) for tag in enums.CREntitiesEnum],
        default=enums.CREntitiesEnum.CR_FRM_HEADER.value,
        max_length=50)
    row_id = models.IntegerField()
    fromValue = models.TextField(blank=True, null=True)
    toValue = models.TextField(blank=True, null=True)

    def __str__(self):
        return self.entity

    class Meta:
        db_table = 'change_request_history'


"""
**********************
*** NOTIFICATION TABLES ***
**********************
"""

class Notification(models.Model):
    code = models.CharField(
        unique=True,
        max_length=255)  # primary key
    form_code = models.ForeignKey(
        ChangeRequestFormHeader,
        on_delete=models.DO_NOTHING,
        to_field='form_code')
    app = models.ForeignKey(
        Application,
        on_delete=models.DO_NOTHING,
        to_field='code')
    notif_type = models.CharField(
        choices=[(tag.value, tag.value) for tag in enums.NotifTypeEnum],
        default=enums.NotifTypeEnum.TASK.value,
        max_length=20
    )
    account_no = models.ForeignKey(
        User,
        on_delete=models.DO_NOTHING,
        to_field='code',
        related_name='receiver_account_no')
    message = models.CharField(
        max_length=255,
        null=True,
        blank=True)
    is_read = models.BooleanField(
        default=False,
        null=True)
    sender_account_no = models.ForeignKey(
        User, on_delete=models.DO_NOTHING,
        to_field='code',
        related_name='sender_account_no')
    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)
    
    class Meta:
        db_table = 'notifications'

    def save(self, *args, **kwargs):
        super(Notification, self).save(*args, **kwargs)
        code = number_generator(enums.GenerateCode.NOTIFICATION.value, self.id)
        Notification.objects.filter(id=self.id).update(code=code)

