import logging

from app.api import api
from app.models import ConsumerApplication, ConsumerApplicationElement, ConsentLog, StatusLogs, ConsumerApplicationFiles, Users
from app.util import datetime_util
from app.util.util import get_or_none
from django.db import transaction as db_transaction

log = logging.getLogger(__name__)


class LoanApplicationAccessor:
    DEFAULT_ADDRESS_TYPE = 'OWNED'

    @classmethod
    def get_loan_application(cls, loan_application_id, user_id=None):
        if user_id:
            return get_or_none(ConsumerApplication, id=loan_application_id, user_id=user_id)
        else:
            return get_or_none(ConsumerApplication, id=loan_application_id)

    @classmethod
    def get_loan_application_for_user(cls, user_id):
        return ConsumerApplication.objects.filter(user_id=user_id).order_by('-created_at').first()

    @classmethod
    def update_aadhar_card(cls, loan_application_id, aadhar_card):
        n_updates = cls.get_loan_application_as_queryset(loan_application_id).update(aadhaar_card=aadhar_card)
        if n_updates == 1:
            return api.SUCCESS

        return api.UNABLE_TO_UPDATE_AADHAR_CARD
    
    @classmethod
    def update_dob(cls, loan_application_id, dob):
        n_updates = cls.get_loan_application_as_queryset(loan_application_id).update(dob=dob)
        if n_updates == 1:
            return api.SUCCESS

        return api.UNABLE_TO_UPDATE_DOB

    @classmethod
    def update_pan_details(cls, loan_application_id, pan, dob, name):
        log.debug(f"Updating loan application id: {loan_application_id} dob: {dob}")
        n_updates = cls.get_loan_application_as_queryset(loan_application_id).update(pan_card=pan, dob=dob, name=name)
        user_id = cls.get_loan_application(loan_application_id).user_id
        m_updates = cls.get_user_as_queryset(user_id).update(name=name)
        if n_updates == 1 and m_updates == 1:
            return api.SUCCESS

        log.error('Unable to pan details')
        return api.UNABLE_TO_UPDATE_PAN_DETAILS

    @classmethod
    def update_references(cls, loan_application_id, reference_name_1, reference_name_2, reference_contact_1,
                          reference_contact_2, reference_relation_1, reference_relation_2):
        is_updated = cls.get_loan_application_element_as_queryset(loan_application_id).update(
            reference_name_1=reference_name_1, reference_name_2=reference_name_2,
            reference_contact_1=reference_contact_1, reference_contact_2=reference_contact_2,
            reference_relation_1=reference_relation_1, reference_relation_2=reference_relation_2)

        if is_updated == 1:
            return api.SUCCESS

        log.error('Unable to update references')
        return api.UNABLE_TO_UPDATE_REFERENCE_DETAILS

    @classmethod
    def get_loan_application_as_queryset(cls, loan_application_id):
        return ConsumerApplication.objects.filter(id=loan_application_id)

    @classmethod
    def get_user_as_queryset(cls, user_id):
        return Users.objects.filter(id=user_id)

    @classmethod
    def get_agreement_s3_path(cls, loan_application_id):
        agreement_file = ConsumerApplicationFiles.objects.filter(
            consumer_application_id=loan_application_id, file_type="loan_agreement_esign").first()
        if not agreement_file:
            return None
        
        return agreement_file.file_path

    @classmethod
    def get_agreement_file_id(cls, loan_application_id):
        agreement_file = ConsumerApplicationFiles.objects.filter(
            consumer_application_id=loan_application_id, file_type="loan_agreement_esign").first()
        if not agreement_file:
            return None

        return agreement_file.id

    @classmethod
    def get_uploaded_files(cls, loan_application_id, file_types_to_check):
        file_types = list(file_types_to_check.values())
        return list(ConsumerApplicationFiles.objects.filter(consumer_application_id=loan_application_id,
                                                            file_type__in=file_types).values_list(
                                                                'file_type', flat=True).distinct())

    @classmethod
    def get_loan_application_element_as_queryset(cls, loan_application_id):
        return ConsumerApplicationElement.objects.filter(application_id=loan_application_id)

    @classmethod
    def update_permanent_address(cls, loan_application_id, street_1, street_2, state, city, pincode):
        is_updated = cls.get_loan_application_as_queryset(loan_application_id).update(
            permanent_address_line_1=street_1, permanent_address_state=state, permanent_address_city=city,
            permanent_address_pincode=pincode, permanent_address_landmark=street_2,
            permanent_address_status=cls.DEFAULT_ADDRESS_TYPE)

        if is_updated == 1:
            return api.SUCCESS

        log.error('Unable to update address details')
        return api.UNABLE_TO_UPDATE_ADDRESS_DETAILS

    @classmethod
    def update_current_address(cls, loan_application_id, street_1, street_2, state, city, pincode):
        is_updated = cls.get_loan_application_as_queryset(loan_application_id).update(
            current_address_line_1=street_1, current_address_state=state, current_address_city=city,
            current_address_pincode=pincode, current_address_landmark=street_2,
            current_address_status=cls.DEFAULT_ADDRESS_TYPE)

        if is_updated == 1:
            return api.SUCCESS

        log.error('Unable to update address details')
        return api.UNABLE_TO_UPDATE_ADDRESS_DETAILS

    @classmethod
    def update_bank(cls, loan_application_id, bank_id):
        is_updated = cls.get_loan_application_element_as_queryset(loan_application_id).update(bank_id=bank_id)
        if is_updated == 1:
            return api.SUCCESS

        log.error('Unable to update bank details')
        return api.UNABLE_TO_UPDATE_BANK_DETAILS

    @classmethod
    def update_employment_details(cls, application_id, company_id, profession_type, salary):
        time_now = datetime_util.get_now_ist_string()

        # Intentionally saving company id in company name here
        is_updated = cls.get_loan_application_as_queryset(application_id).update(
            company_name=company_id, employee_status=profession_type, monthly_income=salary, updated_at=time_now)

        if is_updated == 1:
            return api.SUCCESS

        return api.UNABLE_TO_UPDATE_EMPLOYMENT_DETAILS

    @classmethod
    def get_loan_application_element(cls, application_id):
        return LoanApplicationAccessor.get_loan_application_element_as_queryset(application_id).first()

    @classmethod
    def update_loan_offer(cls, application_id, offer_id, tenure, status):
        time_now = datetime_util.get_now_ist_string()

        LoanApplicationAccessor.get_loan_application_as_queryset(application_id).update(
            offer_id=offer_id, state=status, tenure=tenure, updated_at=time_now)
        return api.SUCCESS

    @classmethod
    def update_consent(cls, application_id, latitude, longitude, status, user_ip):
        if latitude and longitude:
            time_now = datetime_util.get_now_ist_string()

            ConsentLog.objects.update_or_create(application_id=application_id, defaults={
                'ips': user_ip,
                'latitude': latitude,
                'longitude': longitude,
                'status': status,
                "created_at": time_now,
                "updated_at": time_now
            })
        return api.SUCCESS

    @classmethod
    def get_consent_log(cls, application_id):
        consent_log = ConsentLog.objects.filter(application_id=application_id).first()
        if consent_log:
            return consent_log
        return None

    @classmethod
    def get_vendor_id(cls, loan_application_id):
        consumer_application_element = ConsumerApplicationElement.objects.filter(
            application_id=loan_application_id).first()

        if consumer_application_element:
            return consumer_application_element.vendor_id

        return None

    @classmethod
    def update_state(cls, loan_application_id, state, user_id):
        time_now = datetime_util.get_now_ist_string()

        with db_transaction.atomic():
            StatusLogs.objects.get_or_create(
                application_id=loan_application_id, status=state, user_id=user_id, is_reversed=0, defaults={
                    "created_at": time_now,
                    "updated_at": time_now
                })
            cls.get_loan_application_as_queryset(loan_application_id).update(state=state)

        return api.SUCCESS

    @classmethod
    def check_account_aggregator_status(cls, application_id):
        aa_status = ConsumerApplicationElement.objects.filter(application_id=application_id,
                                                              mandatory_document_list='3_month_bank_statement')
        if aa_status:
            return True
        else:
            return False

    @classmethod
    def check_bank_statement_upload(cls, application_id):
        bank_statement_file = ConsumerApplicationFiles.objects.filter(
            consumer_application_id=application_id, file_type='bank_statement').first()
        aa_file = ConsumerApplicationFiles.objects.filter(
            consumer_application_id=application_id, file_type__contains='Account Aggregator'
        )
        if bank_statement_file or aa_file:
            return True
        return False
