import requests
from django.conf import settings
from datetime import datetime, timedelta
from django.core.exceptions import ObjectDoesNotExist
from .models import CreditEnquiry, CreditReport, KYCEnquiry, KYCReport

# Cache for token
_cached_token = None
_token_expiry = None


def get_crb_token():
    """Get CRB authentication token with caching"""
    global _cached_token, _token_expiry
    
    if _cached_token and _token_expiry and datetime.now() < _token_expiry:
        return _cached_token
    
    url = f"{settings.GNUGRID_API_END_POINT}/v1/oauth/token"
    payload = {
        'grant_type': 'client_credentials',
        'client_id': settings.GNUGRID_CLIENT_ID,
        'client_secret': settings.GNUGRID_CLIENT_SECRET,
    }
    headers = {
        'Content-Type': 'application/json',
        'Accept': 'application/json',
    }
    
    response = requests.post(url, json=payload, headers=headers, verify=False)
    response.raise_for_status()
    
    data = response.json()
    _cached_token = data['access_token']
    _token_expiry = datetime.now() + timedelta(seconds=data['expires_in'] - 60)
    
    return _cached_token


def crb_api_call(data, endpoint):
    """Make authenticated API call to CRB service"""
    token = get_crb_token()
    
    url = f"{settings.GNUGRID_API_END_POINT}{endpoint}"
    headers = {
        'Content-Type': 'application/json',
        'Accept': 'application/json',
        'Authorization': f'Bearer {token}',
    }
    
    response = requests.post(url, json=data, headers=headers, verify=False)
    response.raise_for_status()
    
    return response.json()


# Cache management helpers
def get_or_create_credit_enquiry_cache(organisation, payload):
    """
    Get or create cache for credit enquiry.
    Returns (cache_object, is_cached, api_response)
    """
    try:
        cache = CreditEnquiry.objects.get(
            organisation=organisation,
            identifier=payload['identifier'],
            entity_type=payload['entity_type']
        )
        # Return cached data without calling API
        return cache, True, cache.api_response
    except CreditEnquiry.DoesNotExist:
        # No cache found, will need to call API later
        return None, False, None


def save_credit_enquiry_cache(organisation, payload, api_response):
    """Save or update credit enquiry cache"""
    cache, created = CreditEnquiry.objects.update_or_create(
        organisation=organisation,
        identifier=payload['identifier'],
        entity_type=payload['entity_type'],
        defaults={
            'identification_type': payload['identification_type'],
            'reason': payload['reason'],
            'client_consented': payload['client_consented'],
            'sector': payload.get('sector') or '',
            'format': payload.get('format') or '',
            'pi_code': payload.get('pi_code') or '',
            'api_response': api_response,
        }
    )
    return cache, created


def get_or_create_credit_report_cache(organisation, payload, identifier):
    """
    Get or create cache for credit report.
    Returns (cache_object, is_cached, api_response)
    """
    # If identifier provided, prefer exact match
    if identifier:
        try:
            cache = CreditReport.objects.get(
                organisation=organisation,
                enquiry_id=payload['enquiry_id'],
                identifier=identifier,
                entity_type=payload['entity_type']
            )
            return cache, True, cache.api_response
        except CreditReport.DoesNotExist:
            return None, False, None

    # No identifier provided: try to find any report for the enquiry_id within the org
    cache = CreditReport.objects.filter(
        organisation=organisation,
        enquiry_id=payload['enquiry_id']
    ).first()
    if cache:
        return cache, True, cache.api_response
    return None, False, None


def save_credit_report_cache(organisation, payload, identifier, api_response):
    """Save or update credit report cache"""
    entity_type = payload.get('entity_type')

    # If identifier not provided, attempt to derive it from a previous enquiry cache
    if not identifier:
        # Try to find CreditEnquiry where api_response.enquiry_id == payload['enquiry_id']
        try:
            enquiry = CreditEnquiry.objects.filter(
                organisation=organisation,
                api_response__enquiry_id=payload['enquiry_id']
            ).first()
            if enquiry:
                identifier = enquiry.identifier
        except Exception:
            identifier = identifier or ''

    cache, created = CreditReport.objects.update_or_create(
        organisation=organisation,
        enquiry_id=payload['enquiry_id'],
        identifier=identifier,
        entity_type=entity_type,
        defaults={
            'format': payload.get('format') or '',
            'individual_id': payload.get('individual_id') or '',
            'non_individual_id': payload.get('non_individual_id') or '',
            'api_response': api_response,
        }
    )
    return cache, created


def get_or_create_kyc_enquiry_cache(organisation, payload):
    """
    Get or create cache for KYC enquiry.
    Returns (cache_object, is_cached, api_response)
    """
    try:
        cache = KYCEnquiry.objects.get(
            organisation=organisation,
            identifier=payload['identifier'],
            entity_type=payload['entity_type']
        )
        # Return cached data without calling API
        return cache, True, cache.api_response
    except KYCEnquiry.DoesNotExist:
        # No cache found
        return None, False, None


def save_kyc_enquiry_cache(organisation, payload, api_response):
    """Save or update KYC enquiry cache"""
    cache, created = KYCEnquiry.objects.update_or_create(
        organisation=organisation,
        identifier=payload['identifier'],
        entity_type=payload['entity_type'],
        defaults={
            'identification_type': payload['identification_type'],
            'reason': payload['reason'],
            'client_consented': payload['client_consented'],
            'sector': payload.get('sector') or '',
            'format': payload.get('format') or '',
            'pi_code': payload.get('pi_code') or '',
            'api_response': api_response,
        }
    )
    return cache, created


def get_or_create_kyc_report_cache(organisation, payload, identifier):
    """
    Get or create cache for KYC report.
    Returns (cache_object, is_cached, api_response)
    """
    # If identifier provided, prefer exact match
    if identifier:
        try:
            cache = KYCReport.objects.get(
                organisation=organisation,
                enquiry_id=payload['enquiry_id'],
                identifier=identifier,
                entity_type=payload['entity_type']
            )
            return cache, True, cache.api_response
        except KYCReport.DoesNotExist:
            return None, False, None

    # No identifier provided: try to find any report for the enquiry_id within the org
    cache = KYCReport.objects.filter(
        organisation=organisation,
        enquiry_id=payload['enquiry_id']
    ).first()
    if cache:
        return cache, True, cache.api_response
    return None, False, None


def save_kyc_report_cache(organisation, payload, identifier, api_response):
    """Save or update KYC report cache"""
    entity_type = payload.get('entity_type')

    # If identifier not provided, attempt to derive it from a previous enquiry cache
    if not identifier:
        try:
            enquiry = KYCEnquiry.objects.filter(
                organisation=organisation,
                api_response__enquiry_id=payload['enquiry_id']
            ).first()
            if enquiry:
                identifier = enquiry.identifier
        except Exception:
            identifier = identifier or ''

    cache, created = KYCReport.objects.update_or_create(
        organisation=organisation,
        enquiry_id=payload['enquiry_id'],
        identifier=identifier,
        entity_type=entity_type,
        defaults={
            'format': payload.get('format') or '',
            'individual_id': payload.get('individual_id') or '',
            'non_individual_id': payload.get('non_individual_id') or '',
            'api_response': api_response,
        }
    )
    return cache, created


def get_user_organisation(user):
    """
    Extract organisation from user.
    Returns organisation object or None
    """
    try:
        # Navigate from User -> Staff -> Organisation
        organisation = user.user_staff.staff_organisation
        return organisation
    except (AttributeError, ObjectDoesNotExist):
        return None
