// Best-effort read-only enrichment of Mobilizon events over their public
// GraphQL API. Every field is optional — stock Mobilizon, unreachable origins,
// and schema drift all degrade gracefully at the call site. Unauthenticated:
// we never send cookies or tokens.

import { isPublicHttpsHost } from 'src/lib/url-safety'

const EVENT_QUERY = `
  query Event($uuid: UUID!) {
    event(uuid: $uuid) {
      uuid
      title
      description
      beginsOn
      endsOn
      status
      visibility
      joinOptions
      onlineAddress
      language
      category
      tags { slug title }
      picture { url }
      physicalAddress {
        description
        locality
        region
        country
        postalCode
        street
        geom
      }
      options {
        maximumAttendeeCapacity
        remainingAttendeeCapacity
        showRemainingAttendeeCapacity
        hideNumberOfParticipants
        isOnline
      }
      organizerActor { name preferredUsername avatar { url } url }
      relatedEvents { uuid title beginsOn picture { url } }
      participantStats { going notApproved participant }
    }
  }
`

export type MobilizonAddress = {
  description: string | null
  locality: string | null
  region: string | null
  country: string | null
  postalCode: string | null
  street: string | null
  geom: string | null
}

export type MobilizonTag = { slug: string; title: string }

export type MobilizonRelatedEvent = {
  uuid: string
  title: string
  beginsOn: string | null
  picture: { url: string | null } | null
}

export type MobilizonEventEnrichment = {
  uuid: string
  title: string | null
  description: string | null
  beginsOn: string | null
  endsOn: string | null
  status: string | null
  visibility: string | null
  joinOptions: string | null
  onlineAddress: string | null
  language: string | null
  category: string | null
  tags: MobilizonTag[]
  picture: { url: string | null } | null
  physicalAddress: MobilizonAddress | null
  options: {
    maximumAttendeeCapacity: number | null
    remainingAttendeeCapacity: number | null
    showRemainingAttendeeCapacity: boolean | null
    hideNumberOfParticipants: boolean | null
    isOnline: boolean | null
  } | null
  organizerActor: {
    name: string | null
    preferredUsername: string | null
    avatar: { url: string | null } | null
    url: string | null
  } | null
  relatedEvents: MobilizonRelatedEvent[]
  participantStats: {
    going: number | null
    notApproved: number | null
    participant: number | null
  } | null
}

async function postWithTimeout(
  url: string,
  body: unknown,
  timeout = 5000
): Promise<Response> {
  return Promise.race([
    fetch(url, {
      method: 'POST',
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(body),
    }),
    new Promise<Response>((_, reject) => {
      setTimeout(() => {
        reject(new Error(`Mobilizon request to ${url} timed out after ${timeout}ms`))
      }, timeout)
    }),
  ])
}

export async function fetchMobilizonEvent(
  host: string,
  uuid: string
): Promise<Partial<MobilizonEventEnrichment> | null> {
  try {
    const res = await postWithTimeout(
      `https://${host}/api`,
      { query: EVENT_QUERY, variables: { uuid } },
      5000
    )
    if (!res.ok) return null
    const json = (await res.json()) as { data?: { event?: MobilizonEventEnrichment } }
    return json?.data?.event ?? null
  } catch {
    return null
  }
}

// Best-effort parse of a remote Mobilizon event URL into { host, uuid }.
// Mobilizon event URLs are of the form https://<host>/events/<uuid>.
//
// Refuses non-HTTPS schemes, literal-IP hosts, and `localhost` — a hostile
// composer-pasted URL like `http://192.168.1.1/events/abc` would otherwise
// cause `fetchMobilizonEvent` to POST GraphQL into the user's LAN. The
// device sits inside the user's home network so the SSRF surface is real.
export function parseMobilizonEventUrl(
  url: string
): { host: string; uuid: string } | null {
  try {
    const u = new URL(url)
    if (!isPublicHttpsHost(url)) return null
    const parts = u.pathname.split('/').filter(Boolean)
    const idx = parts.indexOf('events')
    if (idx === -1 || !parts[idx + 1]) return null
    return { host: u.host, uuid: parts[idx + 1] }
  } catch {
    return null
  }
}
