"""NestJS framework plugin for the Auth Core module."""

from __future__ import annotations

import json
import logging
from pathlib import Path
from typing import Any, Dict, List, Mapping

from modules.shared.frameworks import FrameworkPlugin

logger = logging.getLogger(__name__)  # pylint: disable=E1101

_DEFAULT_TSCONFIG: Dict[str, Any] = {
    "compilerOptions": {
        "module": "commonjs",
        "declaration": True,
        "emitDecoratorMetadata": True,
        "experimentalDecorators": True,
        "target": "ES2020",
        "sourceMap": True,
        "outDir": "./dist",
        "baseUrl": "./",
        "skipLibCheck": True,
    }
}

_DEFAULT_PACKAGE_JSON: Dict[str, Any] = {
    "name": "auth-core",
    "version": "1.0.0",
    "description": "Auth core helpers generated by RapidKit",
    "private": True,
    "license": "UNLICENSED",
    "scripts": {
        "build": "nest build",
        "start": "nest start",
        "start:dev": "nest start --watch",
        "test": "jest",
    },
    "dependencies": {
        "@nestjs/common": "^11.1.6",
        "@nestjs/core": "^11.1.6",
        "@nestjs/config": "^4.0.2",
        "joi": "^17.12.0",
    },
    "devDependencies": {
        "typescript": "^5.1.3",
        "ts-node": "^10.9.1",
        "@nestjs/cli": "^11.0.10",
    },
}


class NestJSPlugin(FrameworkPlugin):
    """Generate NestJS authentication helpers."""

    @property
    def name(self) -> str:  # noqa: D401 - short alias
        return "nestjs"

    @property
    def language(self) -> str:  # noqa: D401 - short alias
        return "typescript"

    @property
    def display_name(self) -> str:  # noqa: D401 - short alias
        return "NestJS"

    def get_template_mappings(self) -> Dict[str, str]:
        return {
            "configuration": "templates/variants/nestjs/configuration.ts.j2",
            "service": "templates/variants/nestjs/core.service.ts.j2",
            "module": "templates/variants/nestjs/core.module.ts.j2",
            "controller": "templates/variants/nestjs/core.controller.ts.j2",
            "index": "templates/variants/nestjs/index.ts.j2",
            "validation": "templates/variants/nestjs/validation.ts.j2",
            "health": "templates/variants/nestjs/core.health.ts.j2",
            "routes": "templates/variants/nestjs/core.routes.ts.j2",
            "tests": "templates/variants/nestjs/core.spec.ts.j2",
        }

    def get_output_paths(self) -> Dict[str, str]:
        return {
            "configuration": "src/modules/free/auth/core/configuration.ts",
            "service": "src/modules/free/auth/core/auth-core.service.ts",
            "module": "src/modules/free/auth/core/auth-core.module.ts",
            "controller": "src/modules/free/auth/core/auth-core.controller.ts",
            "index": "src/modules/free/auth/core/index.ts",
            "validation": "src/modules/free/auth/core/config/auth-core.validation.ts",
            "health": "src/modules/free/auth/core/auth-core.health.ts",
            "routes": "src/modules/free/auth/core/auth-core.routes.ts",
            "tests": "tests/modules/free/auth/core/auth-core.spec.ts",
        }

    def get_context_enrichments(self, base_context: Mapping[str, Any]) -> Dict[str, Any]:
        return {
            **base_context,
            "framework": "nestjs",
            "framework_display_name": "NestJS",
            "language": "typescript",
        }

    def validate_requirements(self) -> List[str]:
        return []

    def pre_generation_hook(self, output_dir: Path) -> None:
        base = output_dir / "src" / "modules" / "free" / "auth" / "core"
        (base / "core").mkdir(parents=True, exist_ok=True)
        (base / "config").mkdir(parents=True, exist_ok=True)
        (output_dir / "tests" / "modules" / "free" / "auth" / "core").mkdir(
            parents=True, exist_ok=True
        )
        self._ensure_tsconfig(output_dir / "tsconfig.json")
        self._ensure_package_json(output_dir / "package.json")

    def post_generation_hook(self, output_dir: Path) -> None:  # noqa: ARG002
        return None

    def _ensure_tsconfig(self, tsconfig_path: Path) -> None:
        if tsconfig_path.exists():
            try:
                current = json.loads(tsconfig_path.read_text())
            except json.JSONDecodeError:
                logger.warning("Skipping invalid tsconfig.json while generating auth core")
                return

            compiler = current.setdefault("compilerOptions", {})
            updated = False
            for key, value in _DEFAULT_TSCONFIG["compilerOptions"].items():
                if key not in compiler:
                    compiler[key] = value
                    updated = True

            if updated:
                tsconfig_path.write_text(json.dumps(current, indent=2) + "\n")
                logger.info("Updated tsconfig.json with Auth Core defaults")
            return

        tsconfig_path.write_text(json.dumps(_DEFAULT_TSCONFIG, indent=2) + "\n")
        logger.info("Created tsconfig.json scaffold for Auth Core")

    def _ensure_package_json(self, package_path: Path) -> None:
        if package_path.exists():
            try:
                current = json.loads(package_path.read_text())
            except json.JSONDecodeError:
                logger.warning("Skipping invalid package.json while generating auth core")
                return

            updated = False
            for section, defaults in _DEFAULT_PACKAGE_JSON.items():
                if section not in {"dependencies", "devDependencies", "scripts"}:
                    continue
                bucket = current.setdefault(section, {})
                if not isinstance(bucket, dict):
                    logger.warning("package.json section '%s' is not a mapping; skipping", section)
                    continue
                for key, value in defaults.items():
                    if key not in bucket:
                        bucket[key] = value
                        updated = True

            for scalar_key in ("name", "version", "description", "private", "license"):
                if scalar_key not in current:
                    current[scalar_key] = _DEFAULT_PACKAGE_JSON[scalar_key]
                    updated = True

            if updated:
                package_path.write_text(json.dumps(current, indent=2) + "\n")
                logger.info("Augmented package.json with Auth Core defaults")
            return

        package_path.write_text(json.dumps(_DEFAULT_PACKAGE_JSON, indent=2) + "\n")
        logger.info("Created package.json scaffold for Auth Core")

    def get_documentation_urls(self) -> Dict[str, str]:
        return {
            "nestjs": "https://docs.nestjs.com/",
            "config": "https://docs.nestjs.com/techniques/configuration",
            "security": "https://docs.nestjs.com/security/authentication",
        }

    def get_example_configurations(self) -> Dict[str, Any]:
        return {
            "auth_core": {
                "hash": "pbkdf2",
                "iterations": 390_000,
                "tokenTtl": 1_800,
            }
        }

    def get_dependencies(self) -> List[str]:
        return list(_DEFAULT_PACKAGE_JSON["dependencies"].keys())

    def get_dev_dependencies(self) -> List[str]:
        return list(_DEFAULT_PACKAGE_JSON["devDependencies"].keys())
