#!/usr/bin/env python3
"""fenris-collect: device interrogation and store writes.

This is the root oneshot unit's ExecStart. It reads the device selector
from /etc/fenris/fenris.conf, interrogates the drive via smartctl and sysfs,
and writes the sample to the observation store.

Spec: §8.4, §8.5

When run as a script, uses the fenris package from the installed wheel.
"""
import json
import subprocess
import sys
from datetime import datetime, timezone
from pathlib import Path

# Add the venv to path if running from the installed location
VENV_DIR = Path("/opt/fenris")
if VENV_DIR.exists():
    site_packages = next((VENV_DIR / "lib").glob("python*/site-packages"), None)
    if site_packages:
        sys.path.insert(0, str(site_packages))

from fenris.store import init_store, get_store_path
from fenris.collector import run_collection


CONFIG_PATH = Path("/etc/fenris/fenris.conf")


def load_config() -> dict:
    """Load configuration from /etc/fenris/fenris.conf.

    The file holds exactly one key: the device selector.
    Spec §8.3: re-read every run; no reload path.
    """
    if not CONFIG_PATH.exists():
        raise RuntimeError(f"Configuration file not found: {CONFIG_PATH}")

    config = {}
    try:
        with open(CONFIG_PATH, "r") as f:
            for line in f:
                line = line.strip()
                if not line or line.startswith("#"):
                    continue
                if "=" in line:
                    key, value = line.split("=", 1)
                    config[key.strip()] = value.strip()
    except Exception as e:
        raise RuntimeError(f"Failed to read configuration: {e}")

    if "device" not in config:
        raise RuntimeError("Configuration error: missing 'device' key")

    return config


def interrogate_drive(device: str) -> dict:
    """Interrogate the drive via smartctl.

    Returns the smartctl JSON output.
    Raises RuntimeError on failure.
    """
    result = subprocess.run(
        ["smartctl", "-a", "-j", device],
        capture_output=True,
        text=True,
    )

    if result.returncode != 0:
        raise RuntimeError(
            f"smartctl failed for {device}: {result.stderr}"
        )

    try:
        return json.loads(result.stdout)
    except json.JSONDecodeError as e:
        raise RuntimeError(f"Failed to parse smartctl output: {e}")


def find_nvme_sysfs() -> Path | None:
    """Find the NVMe controller sysfs path."""
    nvme_ctrl = Path("/sys/class/nvme")
    if not nvme_ctrl.exists():
        return None

    for ctrl in sorted(nvme_ctrl.iterdir()):
        if ctrl.name.startswith("nvme"):
            return ctrl
    return None


def main() -> None:
    """Run one collection cycle."""
    try:
        config = load_config()
        device = config["device"]

        # Interrogate the drive
        smartctl_data = interrogate_drive(device)

        # Find sysfs path
        sysfs_path = find_nvme_sysfs()
        if sysfs_path is None:
            raise RuntimeError("No NVMe controller found in sysfs")

        # Inject a simple clock
        class SimpleClock:
            def utcnow(self):
                return datetime.now(timezone.utc)

        clock = SimpleClock()

        # Run collection
        result = run_collection(smartctl_data, sysfs_path, config, clock)

        if result["ok"]:
            print(f"Collection successful: {result['sample_count']} sample(s)")
            sys.exit(0)
        else:
            print(f"Collection failed: {result['error']}", file=sys.stderr)
            sys.exit(1)

    except Exception as e:
        print(f"Collection error: {e}", file=sys.stderr)
        sys.exit(1)


if __name__ == "__main__":
    main()
