"""Sample LMS → IAM integration (spec §4/§9/§13).

Demonstrates how the Loan Management System authenticates against the IAM
platform and authorizes actions using PERMISSIONS (never role names) plus the
data SCOPE carried in the JWT.

Run standalone:  python lms_example.py   (with IAM running and env vars set)

Key rules:
  * Consumer apps NEVER store passwords — they only validate JWTs.
  * Authorize on permissions ("loan.approve"), not role names.
  * Use the `scope` claim to restrict which records a user may see.
"""

from __future__ import annotations

import os

import requests

IAM_BASE_URL = os.environ.get("IAM_BASE_URL", "http://localhost:5000")
# Consumer apps validate tokens via IAM's verify endpoint — never share JWT_SECRET_KEY.


class IamSession:
    """Minimal IAM client: login, authenticated calls, transparent refresh."""

    def __init__(self) -> None:
        self.access_token: str | None = None
        self.refresh_token: str | None = None

    def login(self, username: str, password: str) -> None:
        resp = requests.post(
            f"{IAM_BASE_URL}/api/auth/login",
            json={"username": username, "password": password},
            timeout=10,
        )
        resp.raise_for_status()
        data = resp.json()["data"]
        self.access_token = data["access_token"]
        self.refresh_token = data["refresh_token"]

    def _refresh(self) -> bool:
        resp = requests.post(
            f"{IAM_BASE_URL}/api/auth/refresh",
            json={"refresh_token": self.refresh_token},
            timeout=10,
        )
        if resp.status_code != 200:
            return False
        data = resp.json()["data"]
        self.access_token = data["access_token"]
        self.refresh_token = data["refresh_token"]
        return True

    def get(self, path: str) -> requests.Response:
        """GET with the access token, retrying once after a refresh on 401."""
        headers = {"Authorization": f"Bearer {self.access_token}"}
        resp = requests.get(f"{IAM_BASE_URL}{path}", headers=headers, timeout=10)
        if resp.status_code == 401 and self._refresh():
            headers = {"Authorization": f"Bearer {self.access_token}"}
            resp = requests.get(f"{IAM_BASE_URL}{path}", headers=headers, timeout=10)
        return resp


def verify_jwt(token: str) -> dict:
    """Validate a token via IAM's verify endpoint — no signing key needed."""
    resp = requests.post(
        f"{IAM_BASE_URL}/api/auth/verify",
        headers={"Authorization": f"Bearer {token}"},
        timeout=10,
    )
    resp.raise_for_status()
    return resp.json()["data"]


def has_permission(claims: dict, permission: str) -> bool:
    return permission in claims.get("permissions", [])


def loan_visibility_filter(claims: dict) -> dict:
    """Translate the JWT scope into an LMS data filter.

    OWN_BRANCH means the user sees loans for their office and all descendants.
    Call OfficeService.get_descendant_ids() to expand the office tree.
    """
    scope = claims.get("scope", {})
    level = scope.get("level")
    if level == "GLOBAL":
        return {}  # all records
    if level == "ORGANIZATION":
        return {"organization_id": claims.get("org_id")}
    if level == "OWN_BRANCH":
        return {"office_id": scope.get("office_id")}  # caller expands to descendants
    return {"deny": True}  # unknown scope → see nothing


def main() -> None:
    session = IamSession()
    session.login(os.environ["LMS_USER"],
                  os.environ["LMS_PASS"])

    claims = verify_jwt(session.access_token)

    if not has_permission(claims, "loan.approve"):
        pass
    else:
        pass


if __name__ == "__main__":
    main()
