from __future__ import annotations

import logging
from datetime import date
from decimal import Decimal
from typing import Any

from django.db.models import Sum

from basic_info.models import Employee, Merchant
from . import models as cost_models

logger = logging.getLogger(__name__)


def ensure_category(
    *,
    merchant: Merchant,
    category_key: str,
    category_name: str,
    description: str = '',
) -> cost_models.CostCategory:
    """按 unique_key 查找或创建支出类目。

    这是 Provider 可以反向调用的基础能力。
    """
    normalized_key = str(category_key or '').strip()
    normalized_name = str(category_name or '').strip()
    if not normalized_key:
        raise ValueError('category_key 不能为空')
    if not normalized_name:
        raise ValueError('category_name 不能为空')

    category = cost_models.CostCategory.objects.filter(
        merchant=merchant,
        unique_key=normalized_key,
    ).first()

    if category:
        return category

    return cost_models.CostCategory.objects.create(
        merchant=merchant,
        unique_key=normalized_key,
        name=normalized_name,
        description=description,
    )


def create_cost_entry(
    *,
    merchant: Merchant,
    category: cost_models.CostCategory,
    occurred_at: date,
    amount: Decimal | None = None,
    operator: Employee | None = None,
    image1=None,
    image2=None,
    unit_amount: Decimal | None = None,
    quantity: Decimal | None = None,
    unit_name: str = '',
    source_module: str = '',
    source_id: str = '',
    remarks: str = '',
) -> cost_models.CostEntry:
    """创建一条支出记录。"""
    if category.merchant_id != merchant.id:
        raise ValueError('category 与 merchant 不属于同一商户')
    if operator is not None and operator.merchant_id != merchant.id:
        raise ValueError('operator 与 merchant 不属于同一商户')

    return cost_models.CostEntry.objects.create(
        merchant=merchant,
        category=category,
        amount=amount,
        unit_amount=unit_amount,
        quantity=quantity,
        unit_name=unit_name or '',
        occurred_at=occurred_at,
        operator=operator,
        image1=image1,
        image2=image2,
        source_module=source_module or '',
        source_id=source_id or '',
        remarks=remarks or '',
    )


def collect_from_provider(
    provider: cost_models.CostProviderPort,
    *,
    merchant: Merchant,
    start_date: date,
    end_date: date,
) -> dict[str, Any]:
    """从 Provider 采集成本数据，返回写入统计。"""
    entries = provider.get_cost_entries(merchant=merchant, start_date=start_date, end_date=end_date)
    if not isinstance(entries, list):
        raise TypeError(f'Provider.get_cost_entries 必须返回 list，实际返回 {type(entries).__name__}')

    created_count = 0
    errors: list[dict[str, Any]] = []

    for index, entry in enumerate(entries, start=1):
        if not isinstance(entry, cost_models.CostEntryInput):
            errors.append({
                'index': index,
                'error': f'条目不是 CostEntryInput 实例，实际类型: {type(entry).__name__}',
            })
            continue

        try:
            category = ensure_category(
                merchant=merchant,
                category_key=entry.category_key,
                category_name=entry.category_name,
            )
        except ValueError as exc:
            errors.append({
                'index': index,
                'source_module': entry.source_module,
                'source_id': entry.source_id,
                'error': str(exc),
            })
            continue

        create_cost_entry(
            merchant=merchant,
            category=category,
            amount=entry.amount,
            unit_amount=entry.unit_amount,
            quantity=entry.quantity,
            unit_name=entry.unit_name,
            occurred_at=entry.occurred_at,
            operator=None,  # provider 采集的记录没有经办人
            source_module=entry.source_module,
            source_id=entry.source_id,
            remarks=entry.remarks,
        )
        created_count += 1

    return {
        'provider_category_key': getattr(provider, 'category_key', ''),
        'total_seen': len(entries),
        'created_count': created_count,
        'error_count': len(errors),
        'errors': errors,
    }


def aggregate_by_category(
    *,
    merchant: Merchant,
    start_date: date | None = None,
    end_date: date | None = None,
) -> list[dict[str, Any]]:
    """按支出类目汇总。

    返回按 total_amount 降序排列的结果列表。
    """
    queryset = cost_models.CostEntry.objects.filter(merchant=merchant)

    if start_date is not None:
        queryset = queryset.filter(occurred_at__gte=start_date)
    if end_date is not None:
        queryset = queryset.filter(occurred_at__lte=end_date)

    aggregated = (
        queryset.values(
            'category_id',
            'category__name',
            'category__unique_key',
        )
        .annotate(total_amount=Sum('amount'), entry_count=Sum(1))
        .order_by('-total_amount')
    )

    return [
        {
            'category_id': row['category_id'],
            'category_name': row['category__name'],
            'category_key': row['category__unique_key'],
            'total_amount': str(row['total_amount']),
            'entry_count': row['entry_count'],
        }
        for row in aggregated
    ]