from __future__ import annotations

import json
from dataclasses import dataclass
from pathlib import Path

import yaml


@dataclass(frozen=True)
class AuthCase:
    name: str
    headers: dict[str, str]


def _sanitize_case_name(name: str) -> str:
    name = str(name or "").strip()
    if not name:
        return "case"
    # Keep it filesystem-friendly.
    out = []
    for ch in name:
        if ch.isalnum() or ch in ("-", "_", "."):
            out.append(ch)
        else:
            out.append("-")
    return "".join(out).strip("-") or "case"


def load_auth_matrix(path: str | Path) -> list[AuthCase]:
    """Load an auth matrix config (yaml/json).

    Schema:
      {
        "cases": [
          {"name": "user", "headers": {"Authorization": "Bearer ..."}},
          {"name": "admin", "headers": {"Authorization": "Bearer ..."}}
        ]
      }

    Notes:
      - This is intentionally minimal and explicit.
      - Headers are merged into the base request headers (case wins).
    """

    p = Path(path)
    raw = p.read_text(encoding="utf-8")
    if p.suffix.lower() in {".yaml", ".yml"}:
        data = yaml.safe_load(raw) or {}
    else:
        data = json.loads(raw)

    cases = data.get("cases") if isinstance(data, dict) else None
    if not isinstance(cases, list) or not cases:
        raise ValueError("auth matrix must contain a non-empty 'cases' list")

    out: list[AuthCase] = []
    for i, c in enumerate(cases):
        if not isinstance(c, dict):
            raise ValueError(f"auth case #{i} must be an object")
        name = _sanitize_case_name(c.get("name") or f"case{i+1}")
        headers = c.get("headers") or {}
        if not isinstance(headers, dict):
            raise ValueError(f"auth case '{name}' headers must be an object")
        # stringify values (avoid accidental ints)
        headers2 = {str(k): str(v) for k, v in headers.items() if v is not None}
        out.append(AuthCase(name=name, headers=headers2))

    # Ensure unique names
    seen: set[str] = set()
    uniq: list[AuthCase] = []
    for c in out:
        nm = c.name
        if nm not in seen:
            uniq.append(c)
            seen.add(nm)
        else:
            j = 2
            while f"{nm}-{j}" in seen:
                j += 1
            new = f"{nm}-{j}"
            uniq.append(AuthCase(name=new, headers=c.headers))
            seen.add(new)

    return uniq


def merge_auth_headers(request: dict, auth_case: AuthCase) -> dict:
    req = dict(request)
    base_headers = dict(req.get("headers") or {})
    base_headers.update(auth_case.headers or {})
    req["headers"] = base_headers
    return req
