from datetime import datetime, time
from difflib import SequenceMatcher

from django.core.management.base import BaseCommand, CommandError
from django.db import transaction
from django.utils import timezone

from apps.accounts.models import User
from apps.business.orders.models import Order
from apps.business.products.models import Product
from apps.business.schemes.models import OpportunityBundle


class Command(BaseCommand):
    help = "Create or update a real user and seed the first purchase entry."

    def add_arguments(self, parser):
        parser.add_argument("--name", required=True)
        parser.add_argument("--role", required=True)
        parser.add_argument("--status", required=True)
        parser.add_argument("--bundle", required=True)
        parser.add_argument("--joining-date", required=True, help="Format: DD-MM-YYYY")
        parser.add_argument("--first-product", required=True)
        parser.add_argument("--first-purchase-date", required=True, help="Format: DD-MM-YYYY")
        parser.add_argument("--mobile", required=True)

    @staticmethod
    def _parse_date(date_str: str):
        try:
            return datetime.strptime(date_str, "%d-%m-%Y").date()
        except ValueError as exc:
            raise CommandError(f"Invalid date '{date_str}'. Expected DD-MM-YYYY.") from exc

    @staticmethod
    def _make_reference_id(mobile: str, purchase_date):
        base = f"REAL-{mobile}-{purchase_date.strftime('%Y%m%d')}"
        if not Order.objects.filter(reference_id=base).exists():
            return base
        suffix = 1
        while Order.objects.filter(reference_id=f"{base}-{suffix}").exists():
            suffix += 1
        return f"{base}-{suffix}"

    @staticmethod
    def _normalize(text: str) -> str:
        return "".join(ch for ch in text.lower() if ch.isalnum() or ch.isspace()).strip()

    def handle(self, *args, **options):
        name = options["name"].strip()
        role = options["role"].strip().upper()
        status = options["status"].strip().lower()
        bundle_name = options["bundle"].strip()
        first_product_name = options["first_product"].strip()
        mobile = options["mobile"].strip()

        joining_date = self._parse_date(options["joining_date"])
        first_purchase_date = self._parse_date(options["first_purchase_date"])

        is_active = status == "active"

        with transaction.atomic():
            user, created = User.objects.get_or_create(mobile=mobile)
            user.first_name = name
            user.last_name = ""
            user.company_name = name
            user.role = role
            user.is_active = is_active
            user.is_mobile_verified = True
            user.is_staff = role == "ADMIN"
            user.date_joined = timezone.make_aware(datetime.combine(joining_date, time.min))
            user.save()

            bundle = OpportunityBundle.objects.filter(name__iexact=bundle_name).first()
            if not bundle:
                raise CommandError(f"Bundle '{bundle_name}' not found.")

            product = Product.objects.filter(name__iexact=first_product_name, opportunity_bundle=bundle).first()
            if not product:
                product = Product.objects.filter(name__icontains=first_product_name, opportunity_bundle=bundle).first()
            if not product:
                normalized_target = self._normalize(first_product_name)
                candidates = list(Product.objects.filter(opportunity_bundle=bundle))
                if candidates:
                    scored = sorted(
                        [
                            (
                                SequenceMatcher(
                                    None,
                                    normalized_target,
                                    self._normalize(candidate.name),
                                ).ratio(),
                                candidate,
                            )
                            for candidate in candidates
                        ],
                        key=lambda item: item[0],
                        reverse=True,
                    )
                    if scored[0][0] >= 0.6:
                        product = scored[0][1]
            if not product:
                candidates = list(
                    Product.objects.filter(opportunity_bundle=bundle).values_list("name", flat=True)
                )
                raise CommandError(
                    f"Product '{first_product_name}' not found in bundle '{bundle.name}'. "
                    f"Available products: {candidates}"
                )

            purchase_dt = timezone.make_aware(datetime.combine(first_purchase_date, time(hour=9, minute=0)))
            existing_order = Order.objects.filter(
                user=user,
                product=product,
                created_at__date=first_purchase_date,
            ).first()

            if existing_order:
                order = existing_order
                created_order = False
            else:
                reference_id = self._make_reference_id(mobile, first_purchase_date)
                order = Order.objects.create(
                    user=user,
                    product=product,
                    amount=product.base_cost,
                    status="COMPLETED",
                    reference_id=reference_id,
                    payment_method="cash_payment",
                    payment_mode="FULL",
                    ownership_status="FULL_OWNED",
                    order_type="BUNDLE",
                )
                Order.objects.filter(pk=order.pk).update(created_at=purchase_dt, updated_at=purchase_dt)
                created_order = True

            action = "created" if created else "updated"
            order_action = "created" if created_order else "already exists"
            self.stdout.write(self.style.SUCCESS(
                f"User {action}: {mobile} | Product mapped: {product.name} | First purchase {order_action}."
            ))
