from __future__ import annotations

from decimal import Decimal
from typing import Iterable, List

from django.db import transaction
from django.utils import timezone

from stock import models as stock_models
from . import models


def _normalize_stock_ids(stock_ids: Iterable[int | str]) -> List[int]:
    if not stock_ids:
        raise ValueError('stock_ids 不能为空')
    normalized: List[int] = []
    for raw in stock_ids:
        try:
            value = int(raw)
        except (TypeError, ValueError) as exc:
            raise ValueError('stock_ids 必须为整数数组') from exc
        if value <= 0:
            raise ValueError('stock_ids 必须为正整数数组')
        normalized.append(value)
    return normalized


def _serialize_stock_ids(stock_ids: Iterable[int]) -> str:
    return ','.join(str(value) for value in stock_ids)


def validate_stock_details(*, merchant, stock_ids: Iterable[int | str]) -> List[stock_models.StockChangeDetail]:
    normalized_ids = _normalize_stock_ids(stock_ids)

    details_by_id = stock_models.StockChangeDetail.objects.filter(
        id__in=normalized_ids,
        merchant=merchant,
    ).in_bulk(field_name='id')

    missing_ids = [value for value in normalized_ids if value not in details_by_id]
    if missing_ids:
        raise ValueError(f'库存明细不存在: {missing_ids}')

    consumed_ids = [
        detail.id
        for detail in details_by_id.values()
        if getattr(detail, 'is_consumed', False)
    ]
    if consumed_ids:
        raise ValueError(f'库存明细已被消费: {consumed_ids}')

    frozen_ids = list(
        stock_models.StockFreeze.objects.filter(
            stock_detail_id__in=normalized_ids,
            status=stock_models.StockFreezeStatusEnum.FROZEN,
        ).values_list('stock_detail_id', flat=True)
    )
    if frozen_ids:
        raise ValueError(f'库存明细已被冻结: {list(frozen_ids)}')

    return [details_by_id[value] for value in normalized_ids]


def create_allocation_record(
    *,
    item: models.PreSalesOrderItem,
    stock_ids: Iterable[int | str],
    quantity: Decimal | int | str,
    unit: str | None,
    scanned_by,
    remarks: str | None = None,
) -> models.AllocationRecord:
    if quantity is None or str(quantity) == '':
        raise ValueError('quantity 不能为空')
    try:
        quantity_decimal = Decimal(str(quantity))
    except Exception as exc:
        raise ValueError('quantity 格式不正确') from exc
    if quantity_decimal <= 0:
        raise ValueError('quantity 必须大于 0')

    if not unit:
        unit = getattr(item, 'unit', None)
    unit = (unit or '').strip()
    if not unit:
        raise ValueError('unit 不能为空')

    order = getattr(item, 'pre_sales_order', None)
    if order is None:
        raise ValueError('明细缺少所属预销售单')

    normalized_ids = _normalize_stock_ids(stock_ids)
    details = validate_stock_details(merchant=order.merchant, stock_ids=normalized_ids)

    item_product_id = getattr(item, 'product_id', None)
    if item_product_id:
        mismatch_ids = list(
            stock_models.StockChangeDetail.objects.filter(
                id__in=normalized_ids,
            ).exclude(product_id=item_product_id).values_list('id', flat=True)
        )
        if mismatch_ids:
            raise ValueError(f'库存明细产品不匹配: {mismatch_ids}')

    with transaction.atomic():
        record = models.AllocationRecord.objects.create(
            pre_sales_order_item=item,
            stock_ids=_serialize_stock_ids(normalized_ids),
            quantity=quantity_decimal,
            unit=unit,
            status=models.AllocationRecordStatusEnum.ACTIVE,
            scanned_by=scanned_by,
            remarks=remarks,
        )

        stock_models.StockFreeze.objects.bulk_create(
            [
                stock_models.StockFreeze(
                    merchant=order.merchant,
                    product=detail.product,
                    warehouse=order.warehouse,
                    stock_detail=detail,
                    quantity=detail.quantity,
                    unit=detail.unit,
                    status=stock_models.StockFreezeStatusEnum.FROZEN,
                    frozen_by=getattr(scanned_by, 'sys_user', None),
                    frozen_with=record.id,
                )
                for detail in details
            ]
        )

    return record


def cancel_allocation_record(*, record: models.AllocationRecord) -> models.AllocationRecord:
    return cancel_allocation_record_with_unfreeze(record=record)


def cancel_allocation_record_with_unfreeze(
    *,
    record: models.AllocationRecord,
    cancelled_by=None,
    reason: str | None = None,
) -> models.AllocationRecord:
    with transaction.atomic():
        if record.status != models.AllocationRecordStatusEnum.CANCELLED:
            record.status = models.AllocationRecordStatusEnum.CANCELLED
            record.save(update_fields=['status', 'updated_at'])

        stock_models.StockFreeze.objects.filter(
            frozen_with=record.id,
            status=stock_models.StockFreezeStatusEnum.FROZEN,
        ).update(
            status=stock_models.StockFreezeStatusEnum.CANCELLED,
            cancelled_by=cancelled_by,
            cancelled_at=timezone.now(),
            reason=reason,
        )

    return record
