from functools import lru_cache

from django.db import connections

from apps.business.orders.models import Order
from apps.business.products.models import Product


PRODUCT_PART_PAYMENT_FIELDS = (
    'allow_part_payment',
    'part_payment_percentage',
    'part_payment_cooling_days',
    'part_payment_conversion_product',
)

ORDER_PART_PAYMENT_FIELDS = (
    'payment_mode',
    'ownership_status',
    'is_availability_blocked',
    'part_payment_percentage',
    'part_total_amount',
    'part_paid_amount',
    'part_pending_amount',
    'part_payment_started_at',
    'part_payment_due_at',
    'part_payment_completed_at',
    'part_payment_converted_at',
    'part_payment_conversion_product',
    'part_payment_conversion_notes',
)


@lru_cache(maxsize=32)
def _table_columns(using, table_name):
    connection = connections[using]
    with connection.cursor() as cursor:
        description = connection.introspection.get_table_description(cursor, table_name)
    return frozenset(column.name for column in description)


def _missing_model_fields(using, model, field_names):
    table_columns = _table_columns(using, model._meta.db_table)
    missing = []
    for field_name in field_names:
        field = model._meta.get_field(field_name)
        if field.column not in table_columns:
            missing.append(field_name)
    return tuple(missing)


def model_field_is_available(model, field_name, using='default'):
    return not _missing_model_fields(using, model, (field_name,))


def defer_missing_product_part_payment_fields(queryset, relation_prefix=''):
    missing_fields = _missing_model_fields(queryset.db, Product, PRODUCT_PART_PAYMENT_FIELDS)
    if not missing_fields:
        return queryset
    if relation_prefix:
        return queryset.defer(*(f'{relation_prefix}__{field}' for field in missing_fields))
    return queryset.defer(*missing_fields)


def defer_missing_order_part_payment_fields(queryset, relation_prefix=''):
    missing_fields = _missing_model_fields(queryset.db, Order, ORDER_PART_PAYMENT_FIELDS)
    if not missing_fields:
        return queryset
    if relation_prefix:
        return queryset.defer(*(f'{relation_prefix}__{field}' for field in missing_fields))
    return queryset.defer(*missing_fields)