import kubernetes
from django.core.management.base import BaseCommand
from django_scopes import scopes_disabled

from servala.core.models import ControlPlane, Organization, ServiceInstance


class Command(BaseCommand):
    help = (
        "Sync billing labels and annotations to organization namespaces "
        "and service instances on all or selected control planes."
    )

    def add_arguments(self, parser):
        parser.add_argument(
            "--control-plane",
            type=int,
            action="append",
            dest="control_plane_ids",
            help="Control plane ID to sync (can be specified multiple times). "
            "If not specified, syncs all control planes.",
        )
        parser.add_argument(
            "--dry-run",
            action="store_true",
            help="Show what would be changed without making actual changes.",
        )
        parser.add_argument(
            "--namespaces-only",
            action="store_true",
            help="Only sync organization namespace labels/annotations, skip service instances.",
        )
        parser.add_argument(
            "--instances-only",
            action="store_true",
            help="Only sync service instance annotations, skip namespaces.",
        )

    @scopes_disabled()
    def handle(self, *args, **options):
        control_plane_ids = options.get("control_plane_ids")
        dry_run = options.get("dry_run", False)
        namespaces_only = options.get("namespaces_only", False)
        instances_only = options.get("instances_only", False)

        if namespaces_only and instances_only:
            self.stdout.write(
                self.style.ERROR(
                    "Cannot use both --namespaces-only and --instances-only"
                )
            )
            return

        if dry_run:
            self.stdout.write(self.style.WARNING("DRY RUN - no changes will be made"))

        if control_plane_ids:
            control_planes = ControlPlane.objects.filter(id__in=control_plane_ids)
            if not control_planes.exists():  # pragma: no cover
                self.stdout.write(
                    self.style.ERROR("No control planes found with the specified IDs.")
                )
                return
        else:
            control_planes = ControlPlane.objects.all()

        self.stdout.write(
            f"Syncing billing metadata on {control_planes.count()} control plane(s)..."
        )

        for control_plane in control_planes:
            self.stdout.write(
                f"\nControl Plane: {control_plane.name} (ID: {control_plane.pk})"
            )

            if not instances_only:
                self._sync_namespaces(control_plane, dry_run)

            if not namespaces_only:
                self._sync_instances(control_plane, dry_run)

        self.stdout.write(self.style.SUCCESS("\nSync completed."))

    def _build_namespace_metadata(self, organization):
        labels = {
            "servala.com/organization_id": str(organization.id),
        }
        annotations = {
            "servala.com/organization": organization.name,
            "servala.com/origin": organization.origin.name,
        }

        if organization.billing_entity:
            annotations["servala.com/billing"] = organization.billing_entity.name

            for field in ("company_id", "invoice_id"):
                if value := getattr(organization.billing_entity, f"odoo_{field}"):
                    labels[f"servala.com/erp_{field}"] = str(value)

        if organization.odoo_sale_order_id:
            labels["servala.com/erp_sale_order_id"] = str(
                organization.odoo_sale_order_id
            )

        return labels, annotations

    def _sync_namespaces(self, control_plane, dry_run):
        self.stdout.write("  Syncing organization namespaces...")

        try:
            api_instance = kubernetes.client.CoreV1Api(
                control_plane.get_kubernetes_client()
            )
        except Exception as e:
            self.stdout.write(
                self.style.ERROR(f"    Failed to connect to control plane: {e}")
            )
            return

        organizations = Organization.objects.select_related(
            "origin", "billing_entity"
        ).all()

        synced = 0
        skipped = 0
        errors = 0

        for org in organizations:
            if not org.namespace:
                continue

            try:
                try:
                    api_instance.read_namespace(name=org.namespace)
                except kubernetes.client.ApiException as e:
                    if e.status == 404:
                        skipped += 1
                        continue
                    raise

                labels, annotations = self._build_namespace_metadata(org)

                if dry_run:
                    self.stdout.write(
                        f"    [DRY RUN] Would update namespace {org.namespace}"
                    )
                    self.stdout.write(f"      Labels: {labels}")
                    self.stdout.write(f"      Annotations: {annotations}")
                else:
                    body = {
                        "metadata": {
                            "labels": labels,
                            "annotations": annotations,
                        }
                    }
                    api_instance.patch_namespace(name=org.namespace, body=body)
                    self.stdout.write(f"    Updated namespace {org.namespace}")

                synced += 1

            except Exception as e:
                self.stdout.write(
                    self.style.ERROR(
                        f"    Error syncing namespace {org.namespace}: {e}"
                    )
                )
                errors += 1

        self.stdout.write(
            f"    Namespaces: {synced} synced, {skipped} skipped (not found), {errors} errors"
        )

    def _sync_instances(self, control_plane, dry_run):
        self.stdout.write("  Syncing service instance annotations...")
        instances = ServiceInstance.objects.filter(
            context__control_plane=control_plane
        ).select_related(
            "organization",
            "organization__origin",
            "context",
            "context__control_plane",
            "context__control_plane__cloud_provider",
            "context__service_offering",
            "context__service_offering__service",
            "compute_plan_assignment",
        )

        synced = 0
        skipped = 0
        errors = 0

        for instance in instances:
            try:
                annotations = ServiceInstance._build_billing_annotations(
                    compute_plan_assignment=instance.compute_plan_assignment,
                    control_plane=instance.context.control_plane,
                    instance_name=instance.name,
                    display_name=instance.display_name,
                    organization=instance.organization,
                    service=instance.context.service_offering.service,
                )

                if not annotations:
                    skipped += 1
                    continue

                if dry_run:
                    self.stdout.write(
                        f"    [DRY RUN] Would update instance {instance.name} "
                        f"(org: {instance.organization.name})"
                    )
                    self.stdout.write(f"      Annotations: {annotations}")
                else:
                    api_instance = instance.context.control_plane.custom_objects_api
                    patch_body = {"metadata": {"annotations": annotations}}

                    try:
                        api_instance.patch_namespaced_custom_object(
                            group=instance.context.group,
                            version=instance.context.version,
                            namespace=instance.organization.namespace,
                            plural=instance.context.kind_plural,
                            name=instance.name,
                            body=patch_body,
                        )
                        self.stdout.write(
                            f"    Updated instance {instance.name} "
                            f"(org: {instance.organization.name})"
                        )
                    except kubernetes.client.ApiException as e:
                        if e.status == 404:
                            self.stdout.write(
                                self.style.WARNING(
                                    f"    Instance {instance.name} not found in Kubernetes "
                                    f"(org: {instance.organization.name})"
                                )
                            )
                            skipped += 1
                            continue
                        raise

                synced += 1

            except Exception as e:
                self.stdout.write(
                    self.style.ERROR(
                        f"    Error syncing instance {instance.name} "
                        f"(org: {instance.organization.name}): {e}"
                    )
                )
                errors += 1

        self.stdout.write(
            f"    Instances: {synced} synced, {skipped} skipped, {errors} errors"
        )
