"""
Auto-setup of Pyright type stubs for DRF validated_data narrowing.

Called automatically from DjangoCfgConfig.ready() once per project per version.
Writes typings/rest_framework/serializers.pyi and patches pyrightconfig.json
so that Pyright picks up the override without any manual configuration.

How it works:
  1. Finds the project root (directory containing manage.py or pyrightconfig.json)
  2. Copies bundled stubs from django_cfg/stubs/ -> <project_root>/typings/
  3. Ensures pyrightconfig.json has "stubPath": "typings" (highest Pyright priority)
  4. Writes a sentinel file .django_cfg_types_<version> to skip on next startup
     and removes old sentinel files from previous versions
"""

from __future__ import annotations

import json
import logging
import shutil
from pathlib import Path

logger = logging.getLogger(__name__)

_SENTINEL_PREFIX = ".django_cfg_types_"


def _get_version() -> str:
    try:
        from importlib.metadata import version
        return version("django-cfg")
    except Exception:
        return "unknown"


def _find_project_root() -> Path:
    """Walk up from cwd to find project root (contains manage.py or pyrightconfig.json)."""
    cwd = Path.cwd()
    for directory in [cwd, *cwd.parents]:
        if (directory / "manage.py").exists() or (directory / "pyrightconfig.json").exists():
            return directory
        if directory == directory.parent or directory == Path.home():
            break
    return cwd


def _get_stubs_source() -> Path:
    """Return path to bundled stubs inside django_cfg package."""
    return Path(__file__).parent.parent.parent / "stubs"


def _copy_stubs(project_root: Path) -> bool:
    """Copy bundled stubs to project typings/ directory. Returns True if any file was written."""
    stubs_source = _get_stubs_source()
    if not stubs_source.exists():
        return False

    typings_dir = project_root / "typings"
    changed = False

    for src_file in stubs_source.rglob("*.pyi"):
        rel = src_file.relative_to(stubs_source)
        dst_file = typings_dir / rel

        if dst_file.exists() and dst_file.read_bytes() == src_file.read_bytes():
            continue

        dst_file.parent.mkdir(parents=True, exist_ok=True)
        shutil.copy2(src_file, dst_file)
        changed = True

    return changed


def _patch_pyrightconfig(project_root: Path) -> bool:
    """Ensure pyrightconfig.json has stubPath=typings. Returns True if file was changed."""
    config_path = project_root / "pyrightconfig.json"

    if config_path.exists():
        try:
            cfg = json.loads(config_path.read_text())
        except Exception:
            return False
    else:
        cfg = {}

    if cfg.get("stubPath") == "typings":
        return False

    cfg["stubPath"] = "typings"
    config_path.write_text(json.dumps(cfg, indent=2) + "\n")
    return True


def _is_current_version_installed(project_root: Path, version: str) -> bool:
    return (project_root / f"{_SENTINEL_PREFIX}{version}").exists()


def _write_sentinel(project_root: Path, version: str) -> None:
    """Write versioned sentinel, remove old ones from previous versions."""
    # Remove old sentinels
    for old in project_root.glob(f"{_SENTINEL_PREFIX}*"):
        if old.name != f"{_SENTINEL_PREFIX}{version}":
            try:
                old.unlink()
            except Exception:
                pass
    (project_root / f"{_SENTINEL_PREFIX}{version}").write_text(
        "# Generated by django-cfg. Do not edit.\n"
    )


def install(force: bool = False) -> None:
    """
    Install DRF type stubs into the current project.

    Called automatically from DjangoCfgConfig.ready() in development.
    Safe to call multiple times — re-runs only when django-cfg version changes.
    """
    project_root = _find_project_root()
    version = _get_version()

    if not force and _is_current_version_installed(project_root, version):
        return

    try:
        stubs_changed = _copy_stubs(project_root)
        config_changed = _patch_pyrightconfig(project_root)

        if stubs_changed or config_changed:
            logger.info(
                "django-cfg: updated DRF type stubs (v%s). "
                "Restart your IDE / Pyright language server to pick up changes.",
                version,
            )

        _write_sentinel(project_root, version)

    except Exception as exc:
        logger.debug("django-cfg: could not install type stubs: %s", exc)
