#!/usr/bin/env python3
"""Read local version evidence. No installation, network, model contents or mutation."""
import argparse
import hashlib
import importlib.metadata
import json
from pathlib import Path
import re
import shutil
import subprocess


def installed_distribution(name):
    try:
        return importlib.metadata.version(name)
    except importlib.metadata.PackageNotFoundError:
        return None


def ngspice_version():
    executable = shutil.which('ngspice')
    if not executable:
        return None
    try:
        result = subprocess.run([executable, '--version'], stdin=subprocess.DEVNULL,
                                capture_output=True, text=True, timeout=4)
        match = re.search(r'ngspice[-\s]+(\d+(?:\.\d+)*(?:[a-zA-Z0-9.-]*))', result.stdout + result.stderr, re.I)
        return match.group(1) if result.returncode == 0 and match else None
    except (OSError, subprocess.TimeoutExpired):
        return None


def collect(directory):
    root = Path(directory).expanduser().resolve(strict=True)
    if not root.is_dir():
        raise ValueError('pdk-dir must be a directory')
    variant = root.name
    pdk = 'sky130' if re.fullmatch(r'sky130[A-Z]', variant) else None
    sources = root / 'SOURCES'
    revision = None
    if sources.is_file() and sources.stat().st_size <= 65536:
        revision = 'sha256:' + hashlib.sha256(sources.read_bytes()).hexdigest()
    models = {'libs.tech/ngspice/sky130.lib.spice': (root / 'libs.tech/ngspice/sky130.lib.spice').is_file()} if pdk == 'sky130' else {}
    return {'schema': 1, 'observed': {
        'pdk': pdk, 'variant': variant, 'pdk_revision': revision,
        'toolchain': {'klayout': installed_distribution('klayout'), 'openlane': installed_distribution('openlane'), 'ngspice': ngspice_version()},
        'models': models,
    }, 'evidence': {'pdk_revision_basis': 'sha256 of selected SOURCES file; not a foundry release certification',
                    'toolchain_basis': 'Python distribution metadata in this interpreter, and ngspice --version on PATH',
                    'model_basis': 'file presence only, not loaded-model or simulation validation'}}


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--pdk-dir', required=True, help='Selected SKY130 variant directory, such as /path/to/sky130A')
    args = parser.parse_args()
    try:
        print(json.dumps(collect(args.pdk_dir), indent=2))
    except (OSError, ValueError) as error:
        parser.exit(2, 'Cannot read the selected directory: ' + str(error) + '\n')


if __name__ == '__main__':
    main()
