"""
TIIA manual uploader (management command).

This command is intended for one-off backfill / verification:
- Iterate PlateOrder(s)
- Upload all image URLs in plate_image JSON field to Tencent Cloud TIIA gallery (CreateImage)
- Respect QPS limit
- Record failures into PlateOrderTiiaUploadFailure (and continue)
"""

from __future__ import annotations

from datetime import date, datetime, time, timedelta

from django.conf import settings
from django.core.management.base import BaseCommand, CommandError
from django.db import transaction
from django.utils import timezone

from printing.models import PlateOrder, PlateOrderTiiaUploadFailure


def _get_day_range(day: date) -> tuple[datetime, datetime]:
    tz = timezone.get_current_timezone()
    start = datetime.combine(day, time.min)
    end = start + timedelta(days=1)
    if timezone.is_naive(start):
        start = timezone.make_aware(start, tz)
    if timezone.is_naive(end):
        end = timezone.make_aware(end, tz)
    return start, end


@transaction.atomic
def _record_failure(*, run_date: date, plate_order_id: int, error: str, details):
    now = timezone.now()
    obj, created = PlateOrderTiiaUploadFailure.objects.select_for_update().get_or_create(
        run_date=run_date,
        plate_order_id=plate_order_id,
        defaults={
            'error': error or '',
            'details': details or [],
            'attempts': 1,
            'last_attempt_at': now,
        },
    )
    if not created:
        obj.error = error or obj.error or ''
        obj.details = details or obj.details or []
        obj.attempts = (obj.attempts or 0) + 1
        obj.last_attempt_at = now
        obj.save(update_fields=['error', 'details', 'attempts', 'last_attempt_at', 'updated_at'])
    return obj


class Command(BaseCommand):
    help = '手动上传 PlateOrder.plate_image 中的图片到腾讯云 TIIA 图库（CreateImage），带限速与失败落库'

    def add_arguments(self, parser):
        parser.add_argument(
            '--all',
            action='store_true',
            help='处理所有 PlateOrder（建议仅用于一次性跑批/补数据）',
        )
        parser.add_argument(
            '--yesterday',
            action='store_true',
            help='处理“昨天创建”的 PlateOrder（同定时任务筛选口径）',
        )
        parser.add_argument(
            '--date',
            type=str,
            help='处理某一天创建的 PlateOrder（YYYY-MM-DD，本地时区）',
        )
        parser.add_argument(
            '--plate-order-id',
            type=int,
            help='仅处理指定 plate_order_id',
        )
        parser.add_argument(
            '--qps',
            type=float,
            default=None,
            help='覆盖 settings.TENCENTCLOUD_TIIA_QPS（默认 10）',
        )
        parser.add_argument(
            '--dry-run',
            action='store_true',
            help='仅展示将要处理的 PlateOrder 数量，不执行上传',
        )
        parser.add_argument(
            '--limit',
            type=int,
            default=None,
            help='最多处理多少个 PlateOrder（按 id 升序），用于小批量验证',
        )

    def handle(self, *args, **options):
        from api_v1.utils.tencentcloud_tiia import SimpleRateLimiter, upload_plate_order_images_to_tencent_tiia

        do_all = bool(options['all'])
        yesterday = bool(options['yesterday'])
        date_str = options.get('date')
        plate_order_id = options.get('plate_order_id')
        dry_run = bool(options['dry_run'])
        limit = options.get('limit')

        chosen = sum([1 if do_all else 0, 1 if yesterday else 0, 1 if bool(date_str) else 0, 1 if plate_order_id else 0])
        if chosen != 1:
            raise CommandError('必须且只能指定一种模式：--all / --yesterday / --date / --plate-order-id')

        qps_opt = options.get('qps')
        qps = float(qps_opt) if qps_opt is not None else float(getattr(settings, 'TENCENTCLOUD_TIIA_QPS', 10) or 10)
        limiter = SimpleRateLimiter(qps=qps)

        run_date = timezone.localdate()

        qs = PlateOrder.objects.all().only('id', 'plate_image').order_by('id')
        mode = 'all'
        if yesterday:
            mode = 'yesterday'
            day = timezone.localdate() - timedelta(days=1)
            start, end = _get_day_range(day)
            qs = qs.filter(created_at__gte=start, created_at__lt=end)
        elif date_str:
            mode = f'date={date_str}'
            try:
                day = date.fromisoformat(date_str)
            except Exception as e:
                raise CommandError('--date 必须为 YYYY-MM-DD') from e
            start, end = _get_day_range(day)
            qs = qs.filter(created_at__gte=start, created_at__lt=end)
        elif plate_order_id:
            mode = f'plate_order_id={plate_order_id}'
            qs = qs.filter(id=int(plate_order_id))

        if limit is not None:
            qs = qs[: int(limit)]

        total_orders = qs.count() if dry_run else 0
        if dry_run:
            self.stdout.write(f'[TIIA] dry-run: mode={mode} qps={qps} run_date={run_date} total_orders={total_orders}')
            return

        total_orders = 0
        processed_images = 0
        failed_ids: list[int] = []

        self.stdout.write(f'[TIIA] start: mode={mode} qps={qps} run_date={run_date}')
        for po in qs.iterator(chunk_size=200):
            total_orders += 1
            try:
                results = upload_plate_order_images_to_tencent_tiia(
                    plate_order_id=po.id,
                    rate_limiter=limiter,
                )
            except Exception as e:
                failed_ids.append(po.id)
                _record_failure(run_date=run_date, plate_order_id=po.id, error=str(e), details=[{'error': str(e)}])
                self.stderr.write(f'[TIIA] PlateOrder {po.id} 上传异常（已记录，继续）：{e}')
                continue

            processed_images += len(results)
            bad = [r for r in results if not r.get('ok')]
            if bad:
                failed_ids.append(po.id)
                summary = '; '.join([(b.get('error') or '') for b in bad][:5])
                _record_failure(run_date=run_date, plate_order_id=po.id, error=summary, details=bad)
                self.stderr.write(f'[TIIA] PlateOrder {po.id} 部分失败（已记录，继续）：{summary}')

        self.stdout.write(self.style.SUCCESS('[TIIA] done'))
        self.stdout.write(
            str(
                {
                    'run_date': str(run_date),
                    'mode': mode,
                    'qps': qps,
                    'total_orders': total_orders,
                    'processed_images': processed_images,
                    'failed_count': len(failed_ids),
                    'failed_ids_sample': failed_ids[:50],
                }
            )
        )

