from __future__ import annotations

import json
import os
from pathlib import Path


VALID_MODULE_RESOLUTIONS = {"node", "node16", "nodenext", "bundler"}


class TsconfigParser:
    """Parse TypeScript tsconfig.json files to extract module resolution settings."""

    @staticmethod
    def find_tsconfig_for_file(filepath: str | Path) -> str | None:
        """Find tsconfig.json closest to given file by walking up directory tree.

        Args:
            filepath: Path to a file (directory will be checked then walked up)

        Returns:
            Absolute path to closest tsconfig.json, or None if not found
        """
        filepath = Path(filepath).resolve()

        # If filepath is a file, start from its directory
        if filepath.is_file():
            search_dir = filepath.parent
        else:
            search_dir = filepath

        # Walk up the directory tree looking for tsconfig.json
        current_dir = search_dir
        while True:
            tsconfig_path = current_dir / "tsconfig.json"
            if tsconfig_path.exists() and tsconfig_path.is_file():
                return str(tsconfig_path.absolute())

            parent = current_dir.parent
            if parent == current_dir:
                # Reached root
                break
            current_dir = parent

        return None

    @staticmethod
    def parse_tsconfig(filepath: str | Path) -> dict | None:
        """Parse tsconfig.json and return dict.

        Silent error handling: returns None for invalid JSON or parse errors.

        Args:
            filepath: Path to tsconfig.json file

        Returns:
            Parsed config dict, or None if file cannot be parsed
        """
        try:
            with open(filepath, "r", encoding="utf-8") as f:
                content = f.read()
                # Basic JSON parsing (no comments support - standard JSON only)
                config = json.loads(content)
                return config
        except (json.JSONDecodeError, IOError, OSError):
            return None

    @staticmethod
    def get_module_resolution(config: dict) -> str | None:
        """Extract compilerOptions.moduleResolution from tsconfig config.

        Returns only valid values. Returns None for invalid or missing values.

        Args:
            config: Parsed tsconfig dict

        Returns:
            One of: "node", "node16", "nodenext", "bundler", or None
        """
        if not isinstance(config, dict):
            return None

        compiler_options = config.get("compilerOptions")
        if not isinstance(compiler_options, dict):
            return None

        module_resolution = compiler_options.get("moduleResolution")
        if not isinstance(module_resolution, str):
            return None

        # Return only valid values
        if module_resolution in VALID_MODULE_RESOLUTIONS:
            return module_resolution

        return None
