import json
from pathlib import Path


def _candidate_config_dirs() -> list[Path]:
    """
    Candidate config directories for editable installs and packaged wheels.
    """
    here = Path(__file__).resolve().parent
    candidates = [
        here / "configs",                    # packaged wheel layout: orisys/configs/*.json
        here.parent.parent / "config",       # editable install from repo: repo_root/config/*.json
        here.parent / "config",              # fallback
        Path.cwd() / "config",               # script launched from repo root
    ]

    unique: list[Path] = []
    for path in candidates:
        if path not in unique:
            unique.append(path)
    return unique


def _get_config_dir() -> Path:
    for candidate in _candidate_config_dirs():
        if candidate.exists() and any(candidate.glob("*.json")):
            return candidate
    # Default to the first candidate so error messages are stable.
    return _candidate_config_dirs()[0]


CONFIG_DIR = _get_config_dir()


def _config_files() -> list[Path]:
    if not CONFIG_DIR.exists():
        return []
    return sorted(CONFIG_DIR.glob("*.json"))


def _load_json(path: Path) -> dict:
    with open(path, "r", encoding="utf-8") as f:
        return json.load(f)


def load_config(config_name_or_path):
    """
    Load sensor configuration from a JSON file path or config name.

    Config lookup order:
    1. Exact file path (absolute or relative)
    2. File stem under the config directory (e.g. ``ddjx01`` -> ``config/ddjx01.json``)
    3. JSON field ``"name"`` matching the requested name (keeps ``prototype`` compatible)
    """
    config_path = Path(config_name_or_path)

    if config_path.exists() and config_path.is_file():
        if config_path.suffix.lower() != ".json":
            raise ValueError(f"Config file must be a JSON file: {config_path}")
        return _load_json(config_path)

    if config_path.is_absolute():
        raise FileNotFoundError(f"Config file not found at absolute path: {config_path}")

    direct = CONFIG_DIR / f"{config_name_or_path}.json"
    if direct.exists():
        return _load_json(direct)

    # Backward-compatible alias resolution: support old names via JSON "name" field.
    for candidate in _config_files():
        try:
            data = _load_json(candidate)
        except Exception:
            continue
        if data.get("name") == config_name_or_path:
            return data

    available = [f.stem for f in _config_files()]
    available_str = ", ".join(available) if available else "none"
    raise FileNotFoundError(
        f"Input config file not found with name or path: {config_name_or_path}\n"
        f"  Tried as file path: {config_path} (not found)\n"
        f"  Tried as config file: {direct} (not found)\n"
        f"  Available config files: {available_str}"
    )


def list_config():
    """
    List all available JSON configuration files by file stem.
    """
    return [f.stem for f in _config_files()]


def export_config(config_name=None, path="./config/exported_config.json"):
    """
    Export a configuration to a JSON file with readable formatting.
    """
    config = load_config(config_name)
    Path(path).parent.mkdir(parents=True, exist_ok=True)

    with open(path, "w", encoding="utf-8") as f:
        formatted = _format_json_value(config)
        f.write(formatted)
        f.write("\n")

    print(f"Config name: {config_name} exported to {path}")
    return config


def _format_json_value(value, indent_level=0, indent_str="    "):
    if isinstance(value, dict):
        if not value:
            return "{}"
        items = []
        for k, v in value.items():
            key_str = json.dumps(k, ensure_ascii=False)
            value_str = _format_json_value(v, indent_level + 1, indent_str)
            items.append(f"{indent_str * (indent_level + 1)}{key_str}: {value_str}")
        return "{\n" + ",\n".join(items) + "\n" + indent_str * indent_level + "}"
    if isinstance(value, list):
        if not value:
            return "[]"
        items = [json.dumps(item, ensure_ascii=False) for item in value]
        return "[" + ", ".join(items) + "]"
    return json.dumps(value, ensure_ascii=False)
