import { useQuery } from '@tanstack/react-query'
import { resolveUrlViaSearch } from 'src/lib/api'
import type { Status } from 'src/lib/api-types'
import { probeHostSoftware } from 'src/lib/fediverse-probe'
import {
  fetchMobilizonEvent,
  type MobilizonEventEnrichment,
  parseMobilizonEventUrl,
} from 'src/lib/mobilizon-enrichment'
import { queryKeys } from 'src/lib/queries/keys'

export type UrlPastePreview =
  | { kind: 'none' }
  | { kind: 'loading' }
  | { kind: 'local'; status: Status }
  | { kind: 'remote-event'; status: Status }
  | { kind: 'unsupported' }

type ResolveResult = {
  statuses?: Array<Status>
  accounts?: Array<{ id: string }>
  hashtags?: Array<{ name: string }>
}

function isUrl(raw: string): URL | null {
  const trimmed = raw.trim()
  if (!/^https?:\/\//i.test(trimmed)) return null
  try {
    return new URL(trimmed)
  } catch {
    return null
  }
}

// Best-effort conversion of a Mobilizon GraphQL response into a Status-shaped
// object so EventDetailView can render it read-only. No account resolution —
// the organiser is rendered from pf_event.organizers (empty for remote events).
function mobilizonToStatus(url: string, mz: Partial<MobilizonEventEnrichment>): Status {
  const host = (() => {
    try {
      return new URL(url).host
    } catch {
      return ''
    }
  })()

  const placeholderAccount = {
    id: `mobilizon:${host}`,
    acct: mz.organizerActor?.preferredUsername
      ? `${mz.organizerActor.preferredUsername}@${host}`
      : host,
    avatar: mz.organizerActor?.avatar?.url ?? '',
    created_at: mz.beginsOn ?? new Date().toISOString(),
    discoverable: true,
    display_name: mz.organizerActor?.name ?? host,
    followers_count: 0,
    following_count: 0,
    header_bg: null,
    is_admin: false,
    last_fetched_at: null,
    local: false as any,
    location: null as any,
    locked: false,
    note: '',
    note_text: '',
    pronouns: [],
    source: {} as any,
    url: mz.organizerActor?.url ?? url,
    username: mz.organizerActor?.preferredUsername ?? host,
    website: '',
  } as unknown as Status['account']

  return {
    id: `mobilizon:${mz.uuid ?? url}`,
    created_at: mz.beginsOn ?? new Date().toISOString(),
    in_reply_to_id: null,
    in_reply_to_account_id: null,
    sensitive: false,
    spoiler_text: '',
    visibility: 'public',
    language: mz.language ?? null,
    uri: url,
    url,
    reply_count: 0,
    reblogs_count: 0,
    favourites_count: 0,
    edited_at: null,
    favourited: false,
    liked_at: null,
    reblogged: false,
    muted: false,
    bookmarked: false,
    pinned: false,
    filtered: false,
    content: mz.description ?? '',
    reblog: null,
    application: undefined,
    account: placeholderAccount,
    media_attachments: [],
    mentions: [],
    tags: (mz.tags ?? []).map((t) => ({ name: t.title, url: '' })),
    emojis: [],
    card: [],
    poll: null,
    local: false,
    liked_by: null,
    pf_type: 'event',
    pf_source: {
      platform: 'mobilizon',
      remote_url: url,
      software_version: null,
    },
    pf_event: {
      name: mz.title ?? '',
      summary: mz.description ?? null,
      start_time: mz.beginsOn ?? '',
      end_time: mz.endsOn ?? null,
      is_open_ended: !mz.endsOn,
      timezone: null,
      location: {
        name: mz.physicalAddress?.description ?? null,
        address:
          [
            mz.physicalAddress?.street,
            mz.physicalAddress?.locality,
            mz.physicalAddress?.region,
            mz.physicalAddress?.country,
          ]
            .filter((part): part is string => !!part?.trim())
            .join(', ') || null,
        lat: null,
        lon: null,
        online_url: mz.onlineAddress ?? null,
      },
      organizers: [],
      attendees_count: mz.participantStats?.participant ?? null,
      interested_count: null,
      maximum_attendee_capacity: mz.options?.maximumAttendeeCapacity ?? null,
      remaining_attendee_capacity: mz.options?.remainingAttendeeCapacity ?? null,
      join_mode: null,
      external_url: url,
      allow_comments: false,
      allow_remote_interest: false,
      hide_attendee_count: mz.options?.hideNumberOfParticipants ?? false,
      has_attendee_list: false,
      status: (mz.status as any) ?? null,
      my_participation: null,
    },
  } as Status
}

export function useUrlPastePreview(rawQuery: string) {
  const url = isUrl(rawQuery)
  const href = url?.toString() ?? ''
  const host = url?.host ?? ''

  return useQuery<UrlPastePreview>({
    queryKey: queryKeys.halflight.urlPaste(href),
    enabled: !!url,
    staleTime: 10 * 60 * 1000,
    gcTime: 60 * 60 * 1000,
    retry: false,
    refetchOnWindowFocus: false,
    queryFn: async (): Promise<UrlPastePreview> => {
      if (!url) return { kind: 'none' }

      // Always try a local resolve first — the user's Pixelfed may have
      // federated this URL to a local Status ID we can navigate to.
      try {
        const resolved = (await resolveUrlViaSearch(href)) as ResolveResult
        const status = resolved?.statuses?.[0]
        if (status) return { kind: 'local', status }
      } catch {
        // swallow — fall through to remote fallback
      }

      // Shape-based Mobilizon detection: event URLs are unambiguously
      // /events/<uuid>. Skip the nodeinfo probe for this common case — it
      // can fail transiently and the negative cache would poison future
      // lookups for 24h.
      const mobilizonParsed = parseMobilizonEventUrl(href)
      if (mobilizonParsed) {
        const mz = await fetchMobilizonEvent(mobilizonParsed.host, mobilizonParsed.uuid)
        if (mz) return { kind: 'remote-event', status: mobilizonToStatus(href, mz) }
      }

      // Fallback: URL didn't match the known event shape. Probe the host
      // as a last-ditch classifier in case Mobilizon ever serves events
      // under a different path we haven't seen.
      const software = await probeHostSoftware(host)
      const isMobilizon = software?.name?.toLowerCase() === 'mobilizon'
      if (isMobilizon && mobilizonParsed) {
        const mz = await fetchMobilizonEvent(mobilizonParsed.host, mobilizonParsed.uuid)
        if (mz) return { kind: 'remote-event', status: mobilizonToStatus(href, mz) }
      }

      return { kind: 'unsupported' }
    },
  })
}
