# pyright: reportMissingImports=false

"""NestJS framework plugin for the Logging 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,
        "removeComments": True,
        "emitDecoratorMetadata": True,
        "experimentalDecorators": True,
        "allowSyntheticDefaultImports": True,
        "target": "ES2020",
        "sourceMap": True,
        "outDir": "./dist",
        "baseUrl": "./",
        "incremental": True,
        "skipLibCheck": True,
        "strictNullChecks": False,
        "noImplicitAny": False,
        "strictBindCallApply": False,
        "forceConsistentCasingInFileNames": False,
        "noFallthroughCasesInSwitch": False,
    }
}

_DEFAULT_PACKAGE_JSON: Dict[str, Any] = {
    "name": "nestjs-logging",
    "version": "1.0.0",
    "description": "NestJS logging integration generated by RapidKit",
    "author": "",
    "private": True,
    "license": "UNLICENSED",
    "scripts": {
        "build": "nest build",
        "format": 'prettier --write "src/**/*.ts" "test/**/*.ts"',
        "start": "nest start",
        "start:dev": "nest start --watch",
        "start:debug": "nest start --debug --watch",
        "start:prod": "node dist/main",
        "lint": 'eslint "{src,apps,libs,test}/**/*.ts" --fix',
        "test": "jest",
        "test:watch": "jest --watch",
        "test:cov": "jest --coverage",
        "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
        "test:e2e": "jest --config ./test/jest-e2e.json",
    },
    "dependencies": {
        "@nestjs/common": "^11.1.6",
        "@nestjs/core": "^11.1.6",
        "@nestjs/platform-express": "^11.1.6",
        "@nestjs/config": "^4.0.2",
        "joi": "^17.12.0",
        "reflect-metadata": "^0.1.13",
        "rxjs": "^7.8.1",
        "class-validator": "^0.14.0",
        "class-transformer": "^0.5.1",
    },
    "devDependencies": {
        "@nestjs/cli": "^11.0.10",
        "@nestjs/schematics": "^11.0.10",
        "@nestjs/testing": "^11.1.6",
        "@types/express": "^4.17.17",
        "@types/node": "^20.0.0",
        "@types/jest": "^29.5.0",
        "@types/supertest": "^2.0.12",
        "@typescript-eslint/eslint-plugin": "^6.0.0",
        "@typescript-eslint/parser": "^6.0.0",
        "eslint": "^8.42.0",
        "eslint-config-prettier": "^8.8.0",
        "eslint-plugin-prettier": "^4.2.1",
        "jest": "^29.5.0",
        "prettier": "^2.8.8",
        "source-map-support": "^0.5.21",
        "supertest": "^6.3.3",
        "ts-jest": "^29.1.0",
        "ts-loader": "^9.4.3",
        "ts-node": "^10.9.1",
        "tsconfig-paths": "^4.2.1",
        "typescript": "^5.1.3",
    },
}


class NestJSPlugin(FrameworkPlugin):
    """Plugin for generating NestJS-specific logging integration."""

    @property
    def name(self) -> str:
        return "nestjs"

    @property
    def language(self) -> str:
        return "typescript"

    @property
    def display_name(self) -> str:
        return "NestJS"

    def get_template_mappings(self) -> Dict[str, str]:
        return {
            "configuration": "templates/variants/nestjs/configuration.ts.j2",
            "service": "templates/variants/nestjs/logging.service.ts.j2",
            "module": "templates/variants/nestjs/logging.module.ts.j2",
            "controller": "templates/variants/nestjs/logging.controller.ts.j2",
            "index": "templates/variants/nestjs/index.ts.j2",
            "validation": "templates/variants/nestjs/validation.ts.j2",
            "interceptor": "templates/variants/nestjs/logging.interceptor.ts.j2",
            "health": "templates/vendor/nestjs/logging.health.ts.j2",
            # Health templates are now provided as vendor payloads; final project
            # health artefacts will be copied from .rapidkit/vendor if available.
            "integration_tests": "templates/tests/integration/logging.integration.spec.ts.j2",
            "e2e_tests": "templates/variants/nestjs/tests/logging.e2e-spec.ts",
        }

    def get_output_paths(self) -> Dict[str, str]:
        return {
            "configuration": "src/modules/free/essentials/logging/configuration.ts",
            "service": "src/modules/free/essentials/logging/logging.service.ts",
            "module": "src/modules/free/essentials/logging/logging.module.ts",
            "controller": "src/modules/free/essentials/logging/logging.controller.ts",
            "index": "src/modules/free/essentials/logging/index.ts",
            "validation": "src/modules/free/essentials/logging/validation.ts",
            "interceptor": "src/modules/free/essentials/logging/logging.interceptor.ts",
            "health": "src/modules/free/essentials/logging/logging.health.ts",
            "integration_tests": "tests/modules/integration/essentials/logging/logging.integration.spec.ts",
            "e2e_tests": "tests/modules/e2e/free/essentials/logging/logging.e2e-spec.ts",
        }

    def get_context_enrichments(self, base_context: Mapping[str, Any]) -> Dict[str, Any]:
        context = dict(base_context)
        module_name = str(
            context.get("module_name") or context.get("rapidkit_vendor_module") or "logging"
        )
        module_segment = module_name.split("/")[-1]

        context.setdefault("module_slug", module_segment)
        context.setdefault("module_kebab", module_segment.replace("_", "-"))
        context.setdefault("module_basename", module_segment)

        context.update(
            framework="nestjs",
            framework_display_name="NestJS",
            language="typescript",
            vendor_configuration_relative="nestjs/configuration.js",
        )

        return context

    def validate_requirements(self) -> List[str]:
        # For code generation, we don't require external tools to be installed
        # The generated code will require Node.js/npm/TypeScript when used
        # But generation itself only needs the templates to be available
        errors: List[str] = []

        # Check if required templates exist (this would be caught by template resolution anyway)
        # but we can add basic validation here if needed

        return errors

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

        (output_dir / "src" / "health").mkdir(parents=True, exist_ok=True)

    def post_generation_hook(self, _output_dir: Path) -> None:
        # Downstream projects may run linting/formatting; no default action required.
        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("Existing tsconfig.json is invalid JSON; skipping updates")
                return

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

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

        tsconfig_path.parent.mkdir(parents=True, exist_ok=True)
        tsconfig_path.write_text(json.dumps(_DEFAULT_TSCONFIG, indent=2) + "\n")
        logger.info("Created tsconfig.json scaffold for NestJS logging module")

    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("Existing package.json is invalid JSON; skipping updates")
                return

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

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

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

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

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

    def get_example_configurations(self) -> Dict[str, Any]:
        return {
            "logging": {
                "level": "info",
                "format": "json",
                "sinks": ["stderr"],
                "asyncQueue": True,
            }
        }

    def get_dependencies(self) -> List[str]:
        return [
            "@nestjs/common",
            "@nestjs/core",
            "@nestjs/config",
            "joi",
            "reflect-metadata",
            "rxjs",
        ]

    def get_dev_dependencies(self) -> List[str]:
        return [
            "@types/node",
            "@types/jest",
            "@nestjs/testing",
            "typescript",
            "ts-jest",
            "jest",
        ]


class NestJSStandardPlugin(NestJSPlugin):
    """Alias plugin so nestjs.standard resolves to the NestJS implementation."""

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

    @property
    def display_name(self) -> str:
        return "NestJS (standard kit)"
