import { useEffect, useState } from 'react'
import { getJsonWithTimeout } from 'src/requests'
import { AccountStore } from 'src/state/accountStore'
import {
  accountKeys,
  accountsStorage,
  appStorage,
  featureKeyFor,
  getFeatures,
  setFeatures,
} from 'src/state/storage'

export type HalflightFeatureFlag =
  | 'profile'
  | 'quote_posts'
  | 'event_ingest'
  | 'event_rsvp_bridge'
  | 'event_comments_bridge'
  | 'grouped_notifications'
  | 'notification_policies'
  | 'article_ingest'
  | 'pf_source'
  | 'actor_type_relaxed'
  | 'network_timeline_followed'
  | 'tapestry'
  | 'recursive_thread_context'
  | 'recursive_thread_context_anon'
  | 'outbound_replies_collection'
  | 'fetch_all_replies'
  | 'outbox_fetch_on_demand'
  | 'twofactor_intended_redirect'
  | 'oauth_force_login'

export type HalflightFeatures = Record<HalflightFeatureFlag, boolean>

const ALL_FLAGS: readonly HalflightFeatureFlag[] = [
  'profile',
  'quote_posts',
  'event_ingest',
  'event_rsvp_bridge',
  'event_comments_bridge',
  'grouped_notifications',
  'notification_policies',
  'article_ingest',
  'pf_source',
  'actor_type_relaxed',
  'network_timeline_followed',
  'tapestry',
  'recursive_thread_context',
  'recursive_thread_context_anon',
  'outbound_replies_collection',
  'fetch_all_replies',
  'outbox_fetch_on_demand',
  'twofactor_intended_redirect',
  'oauth_force_login',
] as const

function emptyFeatures(): HalflightFeatures {
  const out = {} as HalflightFeatures
  for (const flag of ALL_FLAGS) out[flag] = false
  return out
}

export function parseFeatures(nodeinfo: unknown): HalflightFeatures {
  const meta = (nodeinfo as any)?.metadata
  // Some Halflight-patched Pixelfed builds emit the block at
  // `metadata.halflight.features`; the plan specifies
  // `metadata.config.features.halflight`. Accept either.
  const raw = meta?.halflight?.features ?? meta?.config?.features?.halflight
  const out = emptyFeatures()
  if (!raw || typeof raw !== 'object') return out
  for (const flag of ALL_FLAGS) {
    if (raw[flag] === true) out[flag] = true
  }
  return out
}

export function persistFeatures(instance: string, nodeinfo: unknown): void {
  setFeatures(instance, parseFeatures(nodeinfo))
}

// Best-effort refresh — never throws, never alerts. Safe to fire-and-forget
// from route guards and post-auth hooks.
export async function refreshFeatures(server: string): Promise<void> {
  try {
    const res = await getJsonWithTimeout(
      `https://${server}/api/nodeinfo/2.0.json`,
      undefined,
      false,
      undefined,
      5000
    )
    const json = await res.json()
    if (json) persistFeatures(server, json)
  } catch {
    // swallow — stale cached features are preferred over a hard failure
  }
}

export function useFeature(flag: HalflightFeatureFlag): boolean {
  const read = (): boolean => {
    const instance = AccountStore.activeAccount()?.instance
    if (!instance) return false
    return getFeatures<HalflightFeatures>(instance)?.[flag] === true
  }

  const [enabled, setEnabled] = useState<boolean>(read)

  useEffect(() => {
    // Watch the per-instance feature blob so flag-cache refreshes
    // propagate to mounted components.
    const featuresListener = appStorage.addOnValueChangedListener((changed) => {
      const instance = AccountStore.activeAccount()?.instance
      const watchKey = instance ? featureKeyFor(instance) : null
      if (changed === watchKey) setEnabled(read())
    })
    // Watch the active-account pointer so switching accounts re-resolves
    // the flag against the new account's instance. (`compareBeforeSet` on
    // accountsStorage means listeners only fire on real changes.)
    const accountListener = accountsStorage.addOnValueChangedListener((changed) => {
      if (changed === accountKeys.activeId || changed === accountKeys.list) {
        setEnabled(read())
      }
    })
    return () => {
      featuresListener.remove()
      accountListener.remove()
    }
  }, [flag])

  return enabled
}

/**
 * Whether the active account's instance is a Halflight server (as opposed to
 * vanilla Pixelfed or a Mastodon-shape server). The signal: the Halflight
 * Pixelfed fork patches `app/Util/Site/Nodeinfo.php` to emit a
 * `metadata.halflight` block, and `profile` defaults to true server-side
 * (see FederationController halflight-patch: pinned-federation). Vanilla
 * Pixelfed has no halflight metadata, so its parsed features blob is all
 * false — `useFeature('profile')` is `true` exactly on Halflight servers
 * with the foundational scaffolding active.
 *
 * Use for gating Halflight-only UI surfaces (e.g. the server-news /
 * newsroom row, which depends on the Halflight-only CSRF carve-out for
 * `api/pixelfed/v1/*` POSTs).
 */
export function useIsHalflightServer(): boolean {
  return useFeature('profile')
}
