# recharge/management/commands/load_plans.py
import json
from pathlib import Path
from django.core.management.base import BaseCommand, CommandError
from django.db import transaction
from django.utils import timezone

from recharge.models import PlanCache, Operator, Circle

DEFAULT_PROVIDER_SOURCE = "Goter"

def extract_plans(raw_payload):
    """Return normalized plan list from provider response (case-insensitive)."""
    if not isinstance(raw_payload, dict):
        return []

    lower_map = {k.lower(): v for k, v in raw_payload.items()}
    for key in ("plans", "data", "records"):
        val = lower_map.get(key)
        if isinstance(val, list):
            return val
        if isinstance(val, dict):
            for inner in ("plans", "records", "data"):
                inner_val = val.get(inner)
                if isinstance(inner_val, list):
                    return inner_val
    return []


class Command(BaseCommand):
    help = "Load plan backups (all_plans.json) into PlanCache (upsert)."

    def add_arguments(self, parser):
        parser.add_argument("file", type=str, help="Path to all_plans.json (backup)")

    def handle(self, *args, **options):
        fp = Path(options["file"])
        if not fp.exists():
            raise CommandError(f"File not found: {fp}")

        raw = json.loads(fp.read_text(encoding="utf-8"))

        total = inserted = updated = skipped = 0

        for key, value in raw.items():
            total += 1
            meta = value.get("meta") or {}
            result = value.get("result") or {}

            op_code = meta.get("operator_code") or meta.get("operator")
            circle_code = str(meta.get("circle_code") or meta.get("circle") or "").strip()

            # fallback parse from key like "AD_6"
            if not op_code or not circle_code:
                parts = key.split("_")
                if len(parts) >= 2:
                    op_code = op_code or parts[0]
                    circle_code = circle_code or parts[1]

            if not op_code or not circle_code:
                self.stdout.write(self.style.WARNING(f"Skipping {key}: missing operator/circle info"))
                skipped += 1
                continue

            op_code = str(op_code).strip()
            circle_code = str(circle_code).strip()
            provider_source = DEFAULT_PROVIDER_SOURCE

            # Raw payload (original provider JSON)
            raw_payload = (
                result.get("data") if isinstance(result, dict) and result.get("data") is not None else result
            )
            if not isinstance(raw_payload, dict):
                raw_payload = {"Data": raw_payload} if isinstance(raw_payload, list) else {}

            # Extract status safely
            status_val = None
            for k in ("Status", "status", "STATUS"):
                if k in raw_payload:
                    status_val = raw_payload[k]
                    break
            if not status_val:
                status_val = result.get("status_code") or result.get("status") or "SUCCESS"

            # ✅ Extract plans properly
            plans_list = extract_plans(raw_payload)

            try:
                with transaction.atomic():
                    cache_obj = (
                        PlanCache.objects.select_for_update()
                        .filter(
                            provider_source__iexact=provider_source,
                            operator_code__iexact=op_code,
                            circle_code__iexact=circle_code,
                        )
                        .first()
                    )
                    now = timezone.now()
                    if cache_obj:
                        cache_obj.status = status_val or cache_obj.status
                        cache_obj.plans = plans_list
                        cache_obj.raw_payload = raw_payload
                        try:
                            fetched_at_raw = meta.get("fetched_at")
                            if fetched_at_raw:
                                cache_obj.fetched_at = fetched_at_raw
                        except Exception:
                            pass
                        cache_obj.updated_at = now
                        cache_obj.save(
                            update_fields=["status", "plans", "raw_payload", "updated_at", "fetched_at"]
                        )
                        updated += 1
                    else:
                        PlanCache.objects.create(
                            provider_source=provider_source,
                            operator_code=op_code,
                            circle_code=circle_code,
                            status=status_val or "SUCCESS",
                            plans=plans_list,
                            raw_payload=raw_payload,
                        )
                        inserted += 1
            except Exception as e:
                self.stderr.write(self.style.ERROR(f"Failed to upsert {key} ({op_code}_{circle_code}): {e}"))
                skipped += 1
                continue

        self.stdout.write(
            self.style.SUCCESS(f"Done. total={total} inserted={inserted} updated={updated} skipped={skipped}")
        )


# # recharge/management/commands/load_plans.py
# import json
# from pathlib import Path
# from django.core.management.base import BaseCommand, CommandError
# from django.db import transaction
# from django.utils import timezone

# from recharge.models import PlanCache, Operator, Circle

# # Adjust this if you keep provider names different in your PlanCache (view expects "Goter")
# DEFAULT_PROVIDER_SOURCE = "Goter"

# class Command(BaseCommand):
#     help = "Load plan backups (all_plans.json) into PlanCache (upsert)."

#     def add_arguments(self, parser):
#         parser.add_argument("file", type=str, help="Path to all_plans.json (backup)")

#     def handle(self, *args, **options):
#         fp = Path(options["file"])
#         if not fp.exists():
#             raise CommandError(f"File not found: {fp}")

#         raw = json.loads(fp.read_text(encoding="utf-8"))

#         total = 0
#         inserted = 0
#         updated = 0
#         skipped = 0

#         for key, value in raw.items():
#             total += 1
#             # Defensive access: the backup has meta and result blocks
#             meta = value.get("meta") or {}
#             result = value.get("result") or {}

#             op_code = meta.get("operator_code") or meta.get("operator") or None
#             circle_code = str(meta.get("circle_code") or meta.get("circle") or "").strip()

#             # If meta doesn't have codes, try to parse from key like "AD_6"
#             if not op_code or not circle_code:
#                 parts = key.split("_")
#                 if len(parts) >= 2:
#                     op_code = op_code or parts[0]
#                     circle_code = circle_code or parts[1]

#             if not op_code or not circle_code:
#                 self.stdout.write(self.style.WARNING(f"Skipping {key}: missing operator/circle info"))
#                 skipped += 1
#                 continue

#             # Normalize strings
#             op_code = str(op_code).strip()
#             circle_code = str(circle_code).strip()

#             provider_source = DEFAULT_PROVIDER_SOURCE

#             # Build canonical payload that PlansAPI expects as raw_payload
#             raw_payload = result.get("data") if isinstance(result, dict) and result.get("data") is not None else result

#             # Status normalization: try to extract status inside result.data or result
#             status_val = None
#             if isinstance(raw_payload, dict):
#                 # try common keys (Status, status, STATUS)
#                 for k in ("Status", "status", "STATUS"):
#                     if k in raw_payload:
#                         status_val = raw_payload.get(k)
#                         break
#                 # fallback to top-level status_code/ status
#             if not status_val:
#                 status_val = result.get("status_code") or result.get("status") or "SUCCESS"

#             # Plans array — try the spots your view inspects
#             plans_list = []
#             if isinstance(raw_payload, dict):
#                 for candidate in ("plans", "data", "records"):
#                     if candidate in raw_payload and isinstance(raw_payload[candidate], list):
#                         plans_list = raw_payload[candidate]
#                         break
#             # If raw_payload itself is already a list
#             if isinstance(raw_payload, list):
#                 plans_list = raw_payload

#             # Now upsert PlanCache row
#             try:
#                 with transaction.atomic():
#                     # Try to find existing cache by same provider/operator/circle
#                     cache_obj = (
#                         PlanCache.objects.select_for_update()
#                         .filter(provider_source__iexact=provider_source,
#                                 operator_code__iexact=op_code,
#                                 circle_code__iexact=circle_code)
#                         .first()
#                     )
#                     now = timezone.now()
#                     if cache_obj:
#                         cache_obj.status = status_val or cache_obj.status
#                         cache_obj.plans = plans_list
#                         cache_obj.raw_payload = raw_payload
#                         # set fetched_at only if underlying meta has fetched_at (optional)
#                         try:
#                             fetched_at_raw = meta.get("fetched_at")
#                             if fetched_at_raw:
#                                 # best-effort parse: keep as string so DB JSON / Date handling is safe
#                                 cache_obj.fetched_at = fetched_at_raw
#                         except Exception:
#                             pass
#                         cache_obj.updated_at = now
#                         cache_obj.save(update_fields=["status", "plans", "raw_payload", "updated_at", "fetched_at"])
#                         updated += 1
#                     else:
#                         cache_obj = PlanCache.objects.create(
#                             provider_source=provider_source,
#                             operator_code=op_code,
#                             circle_code=circle_code,
#                             status=status_val or "SUCCESS",
#                             plans=plans_list,
#                             raw_payload=raw_payload,
#                         )
#                         inserted += 1
#             except Exception as e:
#                 self.stderr.write(self.style.ERROR(f"Failed to upsert {key} ({op_code}_{circle_code}): {e}"))
#                 skipped += 1
#                 continue

#         self.stdout.write(self.style.SUCCESS(
#             f"Done. total={total} inserted={inserted} updated={updated} skipped={skipped}"
#         ))
