"""The app's OWN routes — where your product lives.

The baseline ships the smallest thing that proves the wiring works and gives the
conformance pack something concrete to check:

  * GET /api/whoami — auth-required; proves default-deny (401 when anonymous) and
    that identity follows the EFFECTIVE user during a View As. Replace/extend with
    your real product routes; they are protected automatically by the default-deny
    middleware — you do not add an exemption, you add a route.

  * When the app HAS instances, a minimal instance resource so grants + the access
    matrix have real data, and so the 404-vs-403 asymmetry is demonstrated:
      POST /api/instances          — create one (needs `instances.create`)
      GET  /api/instances          — list the ones the caller may see
      GET  /api/instances/{iid}    — one the caller may see, else 404 (never 403,
                                     which would confirm the id exists)

Delete these once you have your own domain objects — an instance is just whatever
your app calls a project / client / workspace, created by your own flow and then
reported to central for the access console.
"""

from fastapi import APIRouter, Body, Depends, HTTPException, Request

from app import accounts, bw_client, bw_config
from app import bw_accounts as bwa
from app.identity import optional_user, real_user

router = APIRouter(prefix="/api", tags=["app"])

_STATUS = {"FORBIDDEN": 403, "NO_SUCH_INSTANCE": 404, "EXISTS": 409,
           "NOT_ASSIGNABLE": 403, "BAD_INPUT": 400, "NO_INSTANCES": 400}


def _raise(exc: bwa.AccountsError):
    raise HTTPException(status_code=_STATUS.get(getattr(exc, "code", "BAD_INPUT"), 400),
                        detail={"error_code": getattr(exc, "code", "BAD_INPUT"),
                                "summary": str(exc)})


@router.get("/whoami")
def whoami(request: Request, user: str | None = Depends(optional_user)) -> dict:
    """The effective user (impersonated during a View As) and the real one. A
    signed-out caller never reaches here — the middleware 401s first."""
    return {
        "username": user,
        "real_user": real_user(request),
        "is_owner": accounts.is_owner(user or ""),
    }


if bw_config.HAS_INSTANCES:

    @router.get("/instances")
    def list_instances(user: str | None = Depends(optional_user)) -> dict:
        return {"instances": accounts.visible_instances(user or "")}

    @router.get("/instances/{iid}")
    def get_instance(iid: str, user: str | None = Depends(optional_user)) -> dict:
        inst = accounts.instance_obj(iid)
        if inst is None or not accounts.has_instance_access(user or "", iid):
            # 404 whether the id is unknown OR merely invisible — a 403 on an
            # invisible instance would confirm the id exists.
            raise HTTPException(status_code=404,
                                detail={"error_code": "NO_SUCH_INSTANCE",
                                        "summary": f"No such {bw_config.INSTANCE_NOUN}."})
        return inst

    def _slugify(name: str) -> str:
        """URL-safe id from a display name: lowercase, alnum runs joined by
        single hyphens ("Culture Foundry!" → "culture-foundry")."""
        out = "".join(c if c.isalnum() else "-" for c in name.lower())
        out = "-".join(p for p in out.split("-") if p)
        return out[:60]

    @router.post("/instances")
    def create_instance(user: str | None = Depends(optional_user),
                        body: dict = Body(...)) -> dict:
        actor = user or ""
        name = (body.get("name") or body.get("label") or body.get("id") or "").strip()
        if not name:
            raise HTTPException(status_code=400,
                                detail={"error_code": "BAD_INPUT",
                                        "summary": "A name is required."})
        # The id is a slug of the name, so instances get clean URLs
        # (/{noun}s/{slug}). Collisions append -2, -3, … per the standard rule.
        base = _slugify(name) or "item"
        existing = {i["id"] for i in bwa.instances()}
        iid = base
        n = 2
        while iid in existing:
            iid = f"{base}-{n}"
            n += 1
        try:
            # create_instance enforces `instances.create` itself (owner passes).
            # grant_creator: a non-owner creator lands on their own creation at
            # the configured level (bw-app.toml `instance_creator_level`; off
            # when empty — they'd need someone with instances.grant to add them).
            bwa.create_instance(
                actor, iid, name, audit=accounts.audit,
                grant_creator=(bw_config.INSTANCE_CREATOR_LEVEL or None))
        except bwa.AccountsError as exc:
            _raise(exc)
        _report_instances()
        return {"success": True, "id": iid, "label": name}


def _report_instances() -> None:
    """Republish the instance catalog to central (display-only, best-effort)."""
    from app.config import get_settings

    if not get_settings().has_bw_client:
        return
    try:
        import app.bw_auth as bw_auth

        bw_auth.report_instances(bwa.instances())
    except Exception:  # noqa: BLE001 - visibility must never break a request
        pass
