"""Generic storage backend contract for canonical time-series stores.

The call sites use this as a small contract layer so SQLite is the executable
backend today while PostgreSQL can be added later without changing callers.
"""
from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path


@dataclass(frozen=True)
class StoreSpec:
    backend: str = "sqlite"
    location: str | Path | None = None

    def normalized_backend(self) -> str:
        backend = (self.backend or "sqlite").strip().lower()
        if backend in {"sqlite", "sqlite3"}:
            return "sqlite"
        if backend in {"postgres", "postgresql", "pg"}:
            return "postgresql"
        return backend


def default_sqlite_store_path(root: Path, default_name: str) -> Path:
    return root / "outputs" / default_name


def normalize_store_spec(
    spec: StoreSpec,
    root: Path,
    *,
    default_sqlite_name: str = "store.db",
) -> tuple[str, Path | str]:
    backend = spec.normalized_backend()
    if backend == "sqlite":
        if spec.location is None:
            return backend, default_sqlite_store_path(root, default_sqlite_name)
        if isinstance(spec.location, Path):
            return backend, spec.location
        location = str(spec.location).strip()
        if location.startswith("sqlite:///"):
            return backend, Path(location.removeprefix("sqlite:///"))
        return backend, Path(location)
    if backend == "postgresql":
        if not spec.location:
            raise ValueError("postgresql backend requires a DSN/location string")
        return backend, str(spec.location)
    raise ValueError(f"unsupported backend: {spec.backend!r}")
