"""`python manage.py run_fixture <name>` — run all .fixture/<name>/*.py scripts.

Discovery:
  - Scans backend/.fixture/<name>/ for *.py files in sorted order.
  - Each file must define a run() function.
  - Scripts execute inside a single transaction (atomic by default).

Usage:
  python manage.py run_fixture solan
  python manage.py run_fixture solan --no-seeds
  python manage.py run_fixture solan --dry-run
"""

from __future__ import annotations

import importlib.util
import sys
from pathlib import Path
from typing import Any

from django.core.management.base import BaseCommand, CommandError
from django.db import transaction


FIXTURE_ROOT = Path(__file__).resolve().parent.parent.parent.parent.parent / ".fixture"


def _load_module(path: Path) -> Any:
    spec = importlib.util.spec_from_file_location(path.stem, path)
    if spec is None or spec.loader is None:
        raise CommandError(f"Cannot load fixture file: {path}")
    mod = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(mod)
    return mod


class Command(BaseCommand):
    help = "Run all .fixture/<name>/*.py scripts (each must expose run())."

    def add_arguments(self, parser):
        parser.add_argument(
            "fixture_name",
            help="Name of the fixture directory under backend/.fixture/",
        )
        parser.add_argument(
            "--no-seeds",
            action="store_true",
            default=False,
            help="Skip running platform seeds before fixtures.",
        )
        parser.add_argument(
            "--dry-run",
            action="store_true",
            default=False,
            help="List fixture scripts that would run without executing them.",
        )

    def handle(self, *args: object, **options: object) -> None:
        name: str = options["fixture_name"]
        dry_run: bool = options["dry_run"]
        no_seeds: bool = options["no_seeds"]

        fixture_dir = FIXTURE_ROOT / name
        if not fixture_dir.exists():
            raise CommandError(f"Fixture directory not found: {fixture_dir}")

        scripts = sorted(fixture_dir.glob("*.py"))
        if not scripts:
            self.stdout.write(self.style.WARNING(f"No fixture scripts found in {fixture_dir}"))
            return

        if dry_run:
            self.stdout.write(self.style.MIGRATE_HEADING(f"Fixture '{name}' — dry run:"))
            if not no_seeds:
                self.stdout.write("  [would run] platform seeds (run_seeds)")
                self.stdout.write("  [would run] python manage.py seed_languages")
            for script in scripts:
                self.stdout.write(f"  [would run] {script.name}")
            return

        self.stdout.write(self.style.MIGRATE_HEADING(f"Running fixture '{name}'…"))

        # 1. Platform seeds first (idempotent)
        if not no_seeds:
            self.stdout.write("\n  Running platform seeds…")
            from django.core.management import call_command
            call_command("seed_languages", stdout=self.stdout, stderr=self.stderr)
            call_command("run_seeds", stdout=self.stdout, stderr=self.stderr)

        # 1b. Re-run seeds after fixture scripts (so per-tenant seeds pick up
        #     newly-created tenants from the first script).
        # We do this by running seeds *again* after fixtures below.

        # 2. Fixture scripts in order, wrapped in one transaction
        self.stdout.write(f"\n  Running {len(scripts)} fixture script(s)…")
        with transaction.atomic():
            for script_path in scripts:
                self.stdout.write(f"\n  → {script_path.name}")
                mod = _load_module(script_path)
                if not hasattr(mod, "run"):
                    raise CommandError(
                        f"{script_path.name} does not define a run() function"
                    )
                mod.run(stdout=self.stdout, style=self.style)

        self.stdout.write(self.style.SUCCESS(f"\n✓ Fixture '{name}' complete."))

        # Re-run seeds so per-tenant seeds catch tenants created by fixtures.
        if not no_seeds:
            self.stdout.write("\n  Re-running platform seeds (post-fixture pass)…")
            call_command("run_seeds", stdout=self.stdout, stderr=self.stderr)
