import { format, isThisYear, isToday, isTomorrow } from "date-fns"
import { fr } from "date-fns/locale"
import { toDate } from "date-fns-tz"

export type VideoConferenceProvider =
  | "meet"
  | "teams"
  | "skype"
  | "zoom"
  | "whatsapp"
  | "signal"
  | "instagram"
  | "discord"
  | "slack"
  | "telegram"
  | "messenger"
  | "jitsi"
  | "phone"
  | "facetime"
  | "other"

/** Icônes `logos:` (carrées quand dispo) — sous-ensemble dans `lib/iconify-logos-vc-subset.json` */
export const VIDEO_CONFERENCE_LOGOS: Record<VideoConferenceProvider, string> = {
  meet: "logos:google-meet",
  teams: "logos:microsoft-teams",
  skype: "logos:skype",
  zoom: "logos:zoom-icon",
  whatsapp: "logos:whatsapp-icon",
  signal: "logos:signal",
  instagram: "logos:instagram-icon",
  discord: "logos:discord-icon",
  slack: "logos:slack-icon",
  telegram: "logos:telegram",
  messenger: "logos:messenger",
  jitsi: "simple-icons:jitsi",
  phone: "logos:twilio-icon",
  facetime: "cbi:iosfacetime",
  other: "logos:webrtc",
}

/** @deprecated Utiliser `VIDEO_CONFERENCE_LOGOS` */
export const VIDEO_CONFERENCE_LIST_LOGOS = VIDEO_CONFERENCE_LOGOS

export interface ParsedCalendarPerson {
  email?: string
  name?: string
}

export interface ParsedCalendarInvitation {
  summary: string
  start: Date
  end: Date
  organizer?: ParsedCalendarPerson
  attendees: ParsedCalendarPerson[]
  location?: string
  description?: string
  conferenceProvider: VideoConferenceProvider
}

function unescapeIcsValue(raw: string): string {
  return raw
    .replace(/\\n/gi, "\n")
    .replace(/\\,/g, ",")
    .replace(/\\;/g, ";")
    .replace(/\\\\/g, "\\")
}

function unfoldIcs(raw: string): string {
  return raw.replace(/\r\n/g, "\n").replace(/\n[\t ]/g, "")
}

function parseIcsParams(left: string): { name: string; params: Record<string, string> } {
  const semi = left.indexOf(";")
  if (semi === -1) {
    return { name: left.toUpperCase(), params: {} }
  }
  const name = left.slice(0, semi).toUpperCase()
  const params: Record<string, string> = {}
  for (const part of left.slice(semi + 1).split(";")) {
    const eq = part.indexOf("=")
    if (eq === -1) continue
    const k = part.slice(0, eq).toUpperCase()
    let v = part.slice(eq + 1)
    if (
      (v.startsWith('"') && v.endsWith('"')) ||
      (v.startsWith("'") && v.endsWith("'"))
    ) {
      v = v.slice(1, -1)
    }
    params[k] = unescapeIcsValue(v)
  }
  return { name, params }
}

/** Parse `20260514T143000Z` or `20260514` + optional TZID */
function parseIcsDateTime(
  valueRaw: string,
  tzid?: string
): Date | null {
  const value = valueRaw.trim()
  const dateOnly = value.match(/^(\d{4})(\d{2})(\d{2})$/)
  if (dateOnly) {
    const y = Number(dateOnly[1])
    const mo = Number(dateOnly[2])
    const d = Number(dateOnly[3])
    return new Date(y, mo - 1, d, 9, 0, 0, 0)
  }

  const dt = value.match(
    /^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})(Z)?$/
  )
  if (!dt) return null
  const [, ys, ms, ds, hs, mins, ss, z] = dt
  const isoLocal = `${ys}-${ms}-${ds}T${hs}:${mins}:${ss}`
  if (z) {
    return new Date(`${isoLocal}Z`)
  }
  if (tzid) {
    try {
      return toDate(isoLocal, { timeZone: tzid })
    } catch {
      return new Date(isoLocal)
    }
  }
  return new Date(isoLocal)
}

function parseMailtoPerson(
  value: string,
  params: Record<string, string>
): ParsedCalendarPerson {
  const m = value.match(/^MAILTO:(.+)$/i)
  const email = m?.[1]?.toLowerCase()
  const cn = params.CN
  return {
    email: email || undefined,
    name: cn || undefined,
  }
}

const CONF_SCAN_ORDER: { id: VideoConferenceProvider; patterns: RegExp[] }[] = [
  { id: "meet", patterns: [/meet\.google\.com/i, /video\.google\.com/i] },
  { id: "teams", patterns: [/teams\.microsoft\.com/i, /teams\.live\.com/i] },
  { id: "zoom", patterns: [/zoom\.us/i, /\.zoom\.us/i] },
  { id: "other", patterns: [/webex\.com/i, /ciscowebex\.com/i] },
  { id: "skype", patterns: [/join\.skype\.com/i, /skype:\/\//i] },
  { id: "jitsi", patterns: [/meet\.jit\.si/i, /8x8\.vc/i, /jitsi\.org/i] },
  { id: "whatsapp", patterns: [/wa\.me/i, /whatsapp\.com\/call/i, /chat\.whatsapp\.com/i] },
  { id: "signal", patterns: [/signal\.me/i, /signal\.group/i] },
  { id: "instagram", patterns: [/instagram\.com/i] },
  { id: "discord", patterns: [/discord\.gg/i, /discord\.com\/channels/i] },
  { id: "slack", patterns: [/slack\.com\/call/i, /slack\.com\/huddle/i] },
  { id: "telegram", patterns: [/t\.me\//i, /telegram\.me\//i] },
  { id: "messenger", patterns: [/m\.me\//i, /messenger\.com/i] },
  { id: "facetime", patterns: [/facetime:/i] },
  { id: "phone", patterns: [/tel:/i] },
]

function detectConferenceProvider(blob: string): VideoConferenceProvider {
  const hay = blob.toLowerCase()
  for (const { id, patterns } of CONF_SCAN_ORDER) {
    for (const re of patterns) {
      if (re.test(hay)) {
        return id
      }
    }
  }
  return "other"
}

function extractVEventCalendar(ics: string): string | null {
  const u = unfoldIcs(ics)
  const start = u.search(/BEGIN:VEVENT/i)
  if (start === -1) return null
  const end = u.indexOf("END:VEVENT", start)
  if (end === -1) return null
  return u.slice(start, end + "END:VEVENT".length)
}

export function parseCalendarInvitationFromIcs(
  icsRaw: string
): ParsedCalendarInvitation | null {
  const block = extractVEventCalendar(icsRaw)
  if (!block) return null

  let summary = "Événement"
  let dtStart: { value: string; tzid?: string } | null = null
  let dtEnd: { value: string; tzid?: string } | null = null
  let organizer: ParsedCalendarPerson | undefined
  const attendees: ParsedCalendarPerson[] = []
  let location: string | undefined
  let description: string | undefined
  let googleConf: string | undefined

  for (const line of block.split("\n")) {
    if (!line || line.startsWith("BEGIN:") || line.startsWith("END:")) continue
    const colon = line.indexOf(":")
    if (colon === -1) continue
    const left = line.slice(0, colon)
    const rawValue = line.slice(colon + 1)
    const { name, params } = parseIcsParams(left)
    const value = unescapeIcsValue(rawValue)

    if (name === "SUMMARY") summary = value || summary
    else if (name === "DTSTART" || name === "DTSTART;VALUE=DATE") {
      dtStart = { value, tzid: params.TZID }
    } else if (name === "DTEND" || name === "DTEND;VALUE=DATE") {
      dtEnd = { value, tzid: params.TZID }
    } else if (name === "ORGANIZER") {
      organizer = parseMailtoPerson(value, params)
    } else if (name === "ATTENDEE") {
      attendees.push(parseMailtoPerson(value, params))
    } else if (name === "LOCATION") location = value
    else if (name === "DESCRIPTION") description = value
    else if (name.startsWith("X-GOOGLE-CONFERENCE")) googleConf = value
  }

  if (!dtStart) return null

  const start = parseIcsDateTime(dtStart.value, dtStart.tzid)
  if (!start || Number.isNaN(start.getTime())) return null

  let end: Date
  if (dtEnd) {
    const parsedEnd = parseIcsDateTime(dtEnd.value, dtEnd.tzid ?? dtStart.tzid)
    end =
      parsedEnd && !Number.isNaN(parsedEnd.getTime())
        ? parsedEnd
        : new Date(start.getTime() + 60 * 60 * 1000)
  } else {
    end = new Date(start.getTime() + 60 * 60 * 1000)
  }

  const blob = [googleConf, location, description, summary].filter(Boolean).join("\n")
  const conferenceProvider = detectConferenceProvider(blob)

  return {
    summary,
    start,
    end,
    organizer,
    attendees,
    location,
    description,
    conferenceProvider,
  }
}

export function extractEmbeddedIcsFromHtml(html: string): string | null {
  const m = html.match(/BEGIN:VCALENDAR[\s\S]*?END:VCALENDAR/i)
  if (!m) return null
  return unfoldIcs(
    m[0]
      .replace(/<br\s*\/?>/gi, "\n")
      .replace(/<\/(p|div|tr)>/gi, "\n")
      .replace(/<[^>]+>/g, "")
      .replace(/&lt;/g, "<")
      .replace(/&gt;/g, ">")
      .replace(/&amp;/g, "&")
      .replace(/&quot;/g, '"')
  )
}

/** Libellé UTC+X stable (évite GMT vs UTC entre Node et le navigateur). */
function formatStableUtcOffsetLabel(date: Date, timeZone: string): string {
  const offset =
    new Intl.DateTimeFormat("en-US", {
      timeZone,
      timeZoneName: "longOffset",
    })
      .formatToParts(date)
      .find((p) => p.type === "timeZoneName")?.value ?? ""

  const normalized = offset.replace(/^GMT/i, "UTC")
  const m = normalized.match(/^UTC([+-])(\d{1,2})(?::(\d{2}))?$/i)
  if (!m) return normalized

  const sign = m[1]
  const hours = Number(m[2])
  const minutes = m[3] ? Number(m[3]) : 0
  if (minutes === 0) return `UTC${sign}${hours}`
  return `UTC${sign}${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}`
}

export function formatInvitationTimeChip(start: Date, end: Date): string {
  const locale = fr
  const tz = Intl.DateTimeFormat().resolvedOptions().timeZone
  const timeFmt = "HH:mm"
  const startLabel = format(start, timeFmt, { locale })
  const endLabel = format(end, timeFmt, { locale })

  let day: string
  if (isToday(start)) day = "Aujourd'hui"
  else if (isTomorrow(start)) day = "Demain"
  else {
    day = isThisYear(start)
      ? format(start, "EEE d MMM", { locale })
      : format(start, "EEE d MMM yyyy", { locale })
  }

  const tzShort = formatStableUtcOffsetLabel(start, tz)
  const range = `${startLabel} – ${endLabel}`
  return tzShort ? `${day} • ${range} (${tzShort})` : `${day} • ${range}`
}

export function formatInvitationAttendeeLine(
  people: ParsedCalendarPerson[],
  maxShow = 3
): string {
  const parts = people
    .map((p) => p.email || p.name)
    .filter(Boolean) as string[]
  const shown = parts.slice(0, maxShow)
  const rest = parts.length - shown.length
  if (rest <= 0) return shown.join(", ")
  return `${shown.join(", ")} et ${rest} autre${rest > 1 ? "s" : ""}`
}
