import { htmlToTextWithLineBreaks } from 'src/utils'
import type { RsvpAction } from './api'
import type {
  Account,
  PfEvent,
  PfEventOrganizer,
  PfEventParticipationStatus,
  PfEventRawOrganizer,
  Status,
} from './api-types'

export function isEventPast(
  e: Pick<PfEvent, 'start_time' | 'end_time' | 'is_open_ended'>
): boolean {
  if (e.is_open_ended) return false
  const end = e.end_time ?? e.start_time
  if (!end) return false
  const t = Date.parse(end)
  return Number.isFinite(t) && t < Date.now()
}

// The server currently projects unhydrated organizers as { actor_uri, name, type }.
// When Halflight finds a matching local Profile it upgrades the entry to a full
// Mastodon-shape Account. Callers branch on this guard.
export function isHydratedAccount(o: PfEventOrganizer): o is Account {
  return typeof (o as Account).id === 'string' && (o as Account).id.length > 0
}

export function rawOrganizerHandle(o: PfEventRawOrganizer): string {
  if (o.name?.trim()) return o.name.trim()
  try {
    const u = new URL(o.actor_uri)
    const path = u.pathname.split('/').filter(Boolean)
    const last = path[path.length - 1] || ''
    const handle = last.replace(/^@/, '')
    return handle ? `@${handle}@${u.hostname}` : u.hostname
  } catch {
    return o.actor_uri
  }
}

// Collapse RFC 5646 / Mobilizon-default "und" (undetermined) and whitespace to
// null so the UI can skip the chip entirely.
export function normalizeLanguage(lang: string | null | undefined): string | null {
  if (!lang) return null
  const v = lang.trim().toLowerCase()
  if (!v || v === 'und' || v === 'undetermined') return null
  return lang.trim()
}

export function rsvpLabel(status: PfEventParticipationStatus | null): string {
  switch (status) {
    case 'going':
      return 'Going'
    case 'going_pending':
      return 'Pending'
    case 'interested':
      return 'Interested'
    case 'declined':
      return "Can't go"
    default:
      return 'RSVP'
  }
}

export function isActiveRsvp(status: PfEventParticipationStatus | null): boolean {
  return status === 'going' || status === 'going_pending' || status === 'interested'
}

// Cycle the primary (tap) action. Matches Mobilizon's ParticipationButton
// state machine — tapping "RSVP" commits to going, tapping "Going" withdraws.
// The long-press menu still offers the full picker.
export function nextRsvpAction(status: PfEventParticipationStatus | null): RsvpAction {
  if (status === null) return 'going'
  return 'withdraw'
}

/**
 * Synthesize a minimal `PfEvent` projection from a vanilla Mastodon `Status`
 * whose author lives on a Mobilizon instance (Phase B §3.1 of the Mastodon
 * follow-up plan). Mastodon strips Pixelfed's `pf_event` extension during
 * federation, so a Mastodon-active client can't get the full event sidecar
 * — but we can still surface the post as an event card (title + canonical
 * URL CTA) by reading the title from the status content and leaving the
 * temporal/location/RSVP fields null. EventCard's `formatEventDate`,
 * `formatMultiDayLabel`, and `locationLabel` all return null on missing
 * data, so the card degrades gracefully to a "this is an event, here's the
 * link" affordance — which is the explicit ceiling for v1.
 */
export function synthesizeMobilizonEventFromMastodonStatus(status: Status): PfEvent {
  const text = htmlToTextWithLineBreaks(status.content ?? '').trim()
  const firstLine = text.split('\n').find((line) => line.trim().length > 0) ?? ''
  const title = firstLine.slice(0, 200) || 'Event'
  return {
    name: title,
    summary: null,
    // Mastodon doesn't expose AP `Event.startTime` on the status entity, so
    // we leave it null and the card formats accordingly.
    start_time: '',
    end_time: null,
    is_open_ended: false,
    timezone: null,
    location: null,
    organizers: [],
    attendees_count: null,
    interested_count: null,
    maximum_attendee_capacity: null,
    remaining_attendee_capacity: null,
    join_mode: null,
    external_url: status.url ?? null,
    allow_comments: false,
    allow_remote_interest: false,
    hide_attendee_count: true,
    has_attendee_list: false,
    status: null,
    my_participation: null,
  }
}

let cachedDeviceTimeZone: string | null | undefined
export function deviceTimeZone(): string | null {
  if (cachedDeviceTimeZone !== undefined) return cachedDeviceTimeZone
  try {
    cachedDeviceTimeZone = new Intl.DateTimeFormat().resolvedOptions().timeZone ?? null
  } catch {
    cachedDeviceTimeZone = null
  }
  return cachedDeviceTimeZone
}
