# FILE: blueprints/ai-tax-agent-bootstrap.yaml
# Authentik Bootstrap (v2025.x): users, groups, scope mappings, OIDC providers, applications

version: 1

metadata:
  name: AI Tax Agent — Bootstrap + OIDC Providers

entries:
  # --- Groups first (so the admin user can reference them) -------------------
  - model: authentik_core.group
    state: present
    identifiers:
      name: "Administrators"
    attrs:
      is_superuser: true

  - model: authentik_core.group
    state: present
    identifiers:
      name: "Tax Reviewers"
    attrs:
      is_superuser: false

  - model: authentik_core.group
    state: present
    identifiers:
      name: "Accountants"
    attrs:
      is_superuser: false

  - model: authentik_core.group
    state: present
    identifiers:
      name: "Clients"
    attrs:
      is_superuser: false

  # --- Admin user ------------------------------------------------------------
  - model: authentik_core.user
    state: present
    identifiers:
      username: admin
    attrs:
      name: "System Administrator"
      email: admin@local.lan
      is_active: true
      is_staff: true
      is_superuser: true
      groups:
        - !Find [authentik_core.group, [name, "Administrators"]]

  # --- Scope mappings (find existing ones and get stable IDs) -----------------
  - id: scope_openid
    model: authentik_providers_oauth2.scopemapping
    identifiers:
      scope_name: openid

  - id: scope_profile
    model: authentik_providers_oauth2.scopemapping
    identifiers:
      scope_name: profile

  - id: scope_email
    model: authentik_providers_oauth2.scopemapping
    identifiers:
      scope_name: email

  - id: scope_groups
    model: authentik_providers_oauth2.scopemapping
    identifiers:
      scope_name: groups

  - id: scope_offline
    model: authentik_providers_oauth2.scopemapping
    identifiers:
      scope_name: offline_access

  # Helper finders
  - id: default_signing_key
    model: authentik_crypto.certificatekeypair
    state: present
    identifiers:
      name: "authentik Self-signed Certificate"

  - id: default_authz_flow
    model: authentik_flows.flow
    state: present
    identifiers:
      slug: "default-authentication-flow"

  - id: default_inval_flow
    model: authentik_flows.flow
    state: present
    identifiers:
      slug: "default-invalidation-flow"

  # ========= OIDC Providers + Applications ==================================

  # --- AI Tax Agent API ------------------------------------------------------
  - model: authentik_providers_oauth2.oauth2provider
    state: present
    identifiers:
      name: "AI Tax Agent API"
    attrs:
      client_id: "ai-tax-agent-api"
      client_secret: !Env [AUTHENTIK_API_CLIENT_SECRET, "changeme-api-secret"]
      authorization_grant_type: "authorization-code"
      client_type: "confidential"
      issuer_mode: "per_provider"
      sub_mode: "hashed_user_id"
      include_claims_in_id_token: true
      signing_key: !KeyOf default_signing_key
      redirect_uris:
        - matching_mode: strict
          url: "https://api.local.lan/auth/callback"
        - matching_mode: strict
          url: "https://review.local.lan/auth/callback"
      scope_mappings:
        - !KeyOf scope_openid
        - !KeyOf scope_profile
        - !KeyOf scope_email
        - !KeyOf scope_groups
        - !KeyOf scope_offline
      authorization_flow: !KeyOf default_authz_flow
      invalidation_flow: !KeyOf default_inval_flow

  - model: authentik_core.application
    state: present
    identifiers:
      slug: "ai-tax-agent-api"
    attrs:
      name: "AI Tax Agent API"
      provider:
        !Find [
          authentik_providers_oauth2.oauth2provider,
          [name, "AI Tax Agent API"],
        ]
      meta_launch_url: "https://api.local.lan"
      meta_description: "AI Tax Agent API Services"
      meta_publisher: "AI Tax Agent"
      policy_engine_mode: "any"

  # --- MinIO -----------------------------------------------------------------
  - model: authentik_providers_oauth2.oauth2provider
    state: present
    identifiers:
      name: "MinIO"
    attrs:
      client_id: "minio"
      client_secret:
        !Env [AUTHENTIK_MINIO_CLIENT_SECRET, "changeme-minio-secret"]
      authorization_grant_type: "authorization-code"
      client_type: "confidential"
      issuer_mode: "per_provider"
      sub_mode: "hashed_user_id"
      include_claims_in_id_token: true
      signing_key: !KeyOf default_signing_key
      redirect_uris:
        - matching_mode: strict
          url: "https://minio.local.lan/oauth_callback"
      scope_mappings:
        - !KeyOf scope_openid
        - !KeyOf scope_profile
        - !KeyOf scope_email
        - !KeyOf scope_groups
        - !KeyOf scope_offline
      authorization_flow: !KeyOf default_authz_flow
      invalidation_flow: !KeyOf default_inval_flow

  - model: authentik_core.application
    state: present
    identifiers:
      slug: "minio"
    attrs:
      name: "MinIO"
      provider:
        !Find [authentik_providers_oauth2.oauth2provider, [name, "MinIO"]]
      meta_launch_url: "https://minio.local.lan"
      meta_description: "Object storage console"
      meta_publisher: "AI Tax Agent"
      policy_engine_mode: "any"

  # --- UI Review (Proxy Provider for ForwardAuth) ---------------------------
  - model: authentik_providers_proxy.proxyprovider
    state: present
    identifiers:
      name: "UI Review Proxy"
    attrs:
      external_host: "https://review.${DOMAIN:-local}"
      internal_host: "http://ui-review:3030"
      authorization_flow: !KeyOf default_authz_flow
      invalidation_flow: !KeyOf default_inval_flow
      mode: "forward_single"
      cookie_domain: "${DOMAIN:-local}"

  - model: authentik_core.application
    state: present
    identifiers:
      slug: "ui-review"
    attrs:
      name: "UI Review"
      provider:
        !Find [
          authentik_providers_proxy.proxyprovider,
          [name, "UI Review Proxy"],
        ]
      meta_launch_url: "https://review.${DOMAIN:-local}"
      meta_description: "Tax Agent Platform - Review UI"
      meta_publisher: "AI Tax Agent"
      policy_engine_mode: "any"

  # --- Vault -----------------------------------------------------------------
  - model: authentik_providers_oauth2.oauth2provider
    state: present
    identifiers:
      name: "Vault"
    attrs:
      client_id: "vault"
      client_secret:
        !Env [AUTHENTIK_VAULT_CLIENT_SECRET, "changeme-vault-secret"]
      authorization_grant_type: "authorization-code"
      client_type: "confidential"
      issuer_mode: "per_provider"
      sub_mode: "hashed_user_id"
      include_claims_in_id_token: true
      signing_key: !KeyOf default_signing_key
      redirect_uris:
        - matching_mode: strict
          url: "https://vault.local.lan/ui/vault/auth/oidc/oidc/callback"
        - matching_mode: strict
          url: "https://vault.local.lan/oidc/callback"
        - matching_mode: strict
          url: "http://localhost:8250/oidc/callback"
      scope_mappings:
        - !KeyOf scope_openid
        - !KeyOf scope_profile
        - !KeyOf scope_email
        - !KeyOf scope_groups
        - !KeyOf scope_offline
      authorization_flow: !KeyOf default_authz_flow
      invalidation_flow: !KeyOf default_inval_flow

  - model: authentik_core.application
    state: present
    identifiers:
      slug: "vault"
    attrs:
      name: "Vault"
      provider:
        !Find [authentik_providers_oauth2.oauth2provider, [name, "Vault"]]
      meta_launch_url: "https://vault.local.lan"
      meta_description: "Secrets management (Vault)"
      meta_publisher: "AI Tax Agent"
      policy_engine_mode: "any"

  # --- Grafana SSO Configuration -------------------------------------------

  # Custom Role Mapping for Grafana
  - model: authentik_providers_oauth2.scopemapping
    state: present
    identifiers:
      name: "Grafana Role Mapping"
    attrs:
      name: "Grafana Role Mapping"
      description: "Maps Authentik groups to Grafana roles"
      scope_name: "role"
      expression: |
        # Map Authentik groups to Grafana roles
        user_groups = [group.name for group in request.user.ak_groups.all()]

        # Admin role mapping
        if "authentik Admins" in user_groups or "Administrators" in user_groups:
            return "Admin"

        # Editor role mapping
        if "Tax Reviewers" in user_groups or "Accountants" in user_groups:
            return "Editor"

        # Default to Viewer role
        return "Viewer"

  # Grafana OAuth2 Provider
  - model: authentik_providers_oauth2.oauth2provider
    state: present
    identifiers:
      name: "Grafana"
    attrs:
      client_id: "grafana"
      client_secret: "${AUTHENTIK_GRAFANA_CLIENT_SECRET:-changeme-grafana-secret}"
      client_type: "confidential"
      redirect_uris: "https://grafana.${DOMAIN:-local.lan}/login/generic_oauth"
      sub_mode: "hashed_user_id"
      include_claims_in_id_token: true
      issuer_mode: "per_provider"
      signing_key:
        !Find [
          authentik_crypto.certificatekeypair,
          [name, "authentik Self-signed Certificate"],
        ]
      property_mappings:
        - !Find [
            authentik_providers_oauth2.scopemapping,
            [scope_name, "openid"],
          ]
        - !Find [authentik_providers_oauth2.scopemapping, [scope_name, "email"]]
        - !Find [
            authentik_providers_oauth2.scopemapping,
            [scope_name, "profile"],
          ]
        - !Find [
            authentik_providers_oauth2.scopemapping,
            [scope_name, "groups"],
          ]
        - !Find [
            authentik_providers_oauth2.scopemapping,
            [name, "Grafana Role Mapping"],
          ]
      authorization_flow: !KeyOf default_authz_flow
      invalidation_flow: !KeyOf default_inval_flow

  # Grafana Application
  - model: authentik_core.application
    state: present
    identifiers:
      slug: "grafana"
    attrs:
      name: "Grafana"
      provider:
        !Find [authentik_providers_oauth2.oauth2provider, [name, "Grafana"]]
      meta_launch_url: "https://grafana.${DOMAIN:-local.lan}"
      meta_description: "Grafana monitoring and observability platform"
      meta_publisher: "Grafana Labs"
      policy_engine_mode: "any"
