// Cached fetch of the signed-in user's profile from platform-api (proxied
// via /api/me on the portal server). One state slot per render so all
// callers share the same payload — middleware fetches once, pages read
// from cache, no per-component re-fetch.

interface MeProfile {
  _id: string
  authentikSubjectId: string
  email: string
  name: string
  role: string
  // Per-tenant role overrides keyed by tenantId; absent keys fall back to
  // `role`. Serialized from platform-api's User.tenantRoles Map.
  tenantRoles?: Record<string, 'owner' | 'admin' | 'member'>
  active: boolean
  platformAdmin: boolean
  tenantIds: string[]
  partnerId?: string
  partner?: { _id: string; slug: string; name: string; status: string }
  lastLoginAt?: string
}

import type { TenantDoc, SubscriptionDoc } from '~/types/workspace'

interface MeResponse {
  profile: MeProfile
  tenants: TenantDoc[]
  subscriptions: SubscriptionDoc[]
}

export function useMe() {
  const state = useState<MeResponse | null>('portal-me', () => null)

  async function fetchMe(force = false): Promise<MeResponse | null> {
    if (state.value && !force) return state.value
    // useRequestFetch on SSR forwards the incoming request's headers
    // (including the nuxt-oidc-auth session cookie) when calling the
    // Nitro route. Bare $fetch on SSR has no cookie context, so /api/me
    // would 401, the middleware would skip the redirect, and the end-user
    // page would flash before client-side rehydration finally redirects.
    const fetcher = useRequestFetch()
    try {
      state.value = await fetcher<MeResponse>('/api/me')
    } catch (err) {
      // If the access token lapsed, the proxy forwards a stale token and
      // platform-api 401s. On the client we can refresh the session silently
      // and retry once (same self-heal as useApiFetch for writes). On the
      // server we can't trigger the OIDC refresh, so fall through to null and
      // let the auth middleware bounce to sign-in.
      const status = (err as { statusCode?: number; response?: { status?: number } })
      const is401 = status?.statusCode === 401 || status?.response?.status === 401
      if (import.meta.client && is401) {
        try {
          await $fetch('/api/_auth/refresh', { method: 'POST', headers: { Accept: 'text/json' } })
          state.value = await fetcher<MeResponse>('/api/me')
          return state.value
        } catch {
          state.value = null
        }
      } else {
        state.value = null
      }
    }
    return state.value
  }

  const profile = computed<MeProfile | null>(() => state.value?.profile ?? null)
  const partner = computed(() => profile.value?.partner ?? null)
  const isPartnerStaff = computed(() => !!profile.value?.partnerId)
  const isPlatformAdmin = computed(() => !!profile.value?.platformAdmin)

  const isAdminRole = (r: string | undefined) => r === 'owner' || r === 'admin'

  // Effective role for a specific tenant — mirrors platform-api roleForTenant():
  // a per-tenant entry wins, else the legacy global `role`, else 'member'.
  function roleForTenant(tenantId: string): 'owner' | 'admin' | 'member' {
    const p = profile.value
    return p?.tenantRoles?.[tenantId] ?? (p?.role as 'owner' | 'admin' | 'member') ?? 'member'
  }
  function isTenantAdminOf(tenantId: string): boolean {
    return isAdminRole(roleForTenant(tenantId))
  }

  // Gates the /admin surface: true if the user administers AT LEAST ONE of
  // their tenants. Per-tenant enforcement of *which* workspace they may admin
  // happens once a tenant is in context (backend membership + roleForTenant).
  // For existing single-role data this is identical to the old global check.
  const isTenantAdmin = computed(() => {
    const p = profile.value
    if (!p) return false
    if (p.tenantIds.length) return p.tenantIds.some((t) => isTenantAdminOf(t))
    return isAdminRole(p.role)
  })

  return {
    state,
    profile,
    partner,
    isPartnerStaff,
    isPlatformAdmin,
    isTenantAdmin,
    roleForTenant,
    isTenantAdminOf,
    fetchMe,
  }
}
