/**
 * Tiny Wikidata Query Service client. SPARQL endpoint, no key required.
 * Used to surface awards (P166) and filming locations (P915) keyed by an
 * item's Wikidata id (TMDB exposes this via external_ids.wikidata_id).
 */

const SPARQL_ENDPOINT = 'https://query.wikidata.org/sparql'

async function sparql<T = unknown>(query: string): Promise<T | null> {
  try {
    const url = `${SPARQL_ENDPOINT}?query=${encodeURIComponent(query)}&format=json`
    const res = await fetch(url, { headers: { Accept: 'application/sparql-results+json' } })
    if (!res.ok) return null
    return (await res.json()) as T
  } catch {
    return null
  }
}

export interface WikidataAward {
  id: string
  label: string
  ceremony?: string
  point_in_time?: string
  for_work?: string
}

export interface WikidataLocation {
  id: string
  label: string
  /** "Point(longitude latitude)" - Wikidata returns coords this way */
  coords?: string
  countryLabel?: string
}

/**
 * Awards won/nominated for the given Wikidata id. Returns label-decorated
 * results, ranked roughly by ceremony recency.
 */
export async function getAwards(qid: string): Promise<WikidataAward[]> {
  if (!qid) return []
  const query = `
    SELECT DISTINCT ?award ?awardLabel ?ceremony ?ceremonyLabel ?pit ?for ?forLabel WHERE {
      wd:${qid} p:P166 ?statement.
      ?statement ps:P166 ?award.
      OPTIONAL { ?statement pq:P585 ?pit. }
      OPTIONAL { ?statement pq:P805 ?ceremony. }
      OPTIONAL { ?statement pq:P1686 ?for. }
      SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
    }
    LIMIT 60
  `
  type Row = {
    award: { value: string }
    awardLabel?: { value: string }
    ceremony?: { value: string }
    ceremonyLabel?: { value: string }
    pit?: { value: string }
    for?: { value: string }
    forLabel?: { value: string }
  }
  const data = await sparql<{ results: { bindings: Row[] } }>(query)
  if (!data?.results?.bindings) return []
  return data.results.bindings.map(r => ({
    id: r.award.value.split('/').pop() || '',
    label: r.awardLabel?.value || 'Award',
    ceremony: r.ceremonyLabel?.value,
    point_in_time: r.pit?.value,
    for_work: r.forLabel?.value,
  }))
}

/**
 * Filming locations for the given Wikidata id. Each location optionally
 * carries coordinates so a downstream map can plot it.
 */
export async function getFilmingLocations(qid: string): Promise<WikidataLocation[]> {
  if (!qid) return []
  const query = `
    SELECT DISTINCT ?loc ?locLabel ?coords ?countryLabel WHERE {
      wd:${qid} wdt:P915 ?loc.
      OPTIONAL { ?loc wdt:P625 ?coords. }
      OPTIONAL { ?loc wdt:P17 ?country. }
      SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
    }
    LIMIT 50
  `
  type Row = {
    loc: { value: string }
    locLabel?: { value: string }
    coords?: { value: string }
    countryLabel?: { value: string }
  }
  const data = await sparql<{ results: { bindings: Row[] } }>(query)
  if (!data?.results?.bindings) return []
  return data.results.bindings.map(r => ({
    id: r.loc.value.split('/').pop() || '',
    label: r.locLabel?.value || 'Location',
    coords: r.coords?.value,
    countryLabel: r.countryLabel?.value,
  }))
}

export interface WikidataAwardWinner {
  /** Wikidata item id of the work (e.g. Q24862 for "The Godfather"). */
  qid: string
  label: string
  /** TMDB id from P4947 (movie) or P4983 (tv). May be null. */
  tmdbId: string | null
  /** "movie" / "tv" hint based on which TMDB id was found. */
  type: 'movie' | 'tv' | null
  year: number | null
}

/**
 * Fetch all winners of a given award (P166) where the award itself is
 * a single Wikidata entity (e.g. wd:Q102427 for Academy Award for Best
 * Picture). Returns labelled rows with TMDB ids when available so the
 * caller can cross-reference with the local library.
 *
 * Capped at 200 rows; awards with bigger histories should be queried
 * with a year filter on the caller side.
 */
export async function getAwardWinners(awardQid: string): Promise<WikidataAwardWinner[]> {
  if (!awardQid) return []
  const query = `
    SELECT DISTINCT ?work ?workLabel ?tmdb ?tmdbTv ?year WHERE {
      ?work wdt:P166 wd:${awardQid}.
      OPTIONAL { ?work wdt:P4947 ?tmdb. }
      OPTIONAL { ?work wdt:P4983 ?tmdbTv. }
      OPTIONAL { ?work wdt:P577 ?date. BIND(YEAR(?date) AS ?year) }
      SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
    }
    ORDER BY DESC(?year)
    LIMIT 200
  `
  type Row = {
    work: { value: string }
    workLabel?: { value: string }
    tmdb?: { value: string }
    tmdbTv?: { value: string }
    year?: { value: string }
  }
  const data = await sparql<{ results: { bindings: Row[] } }>(query)
  if (!data?.results?.bindings) return []
  const seen = new Set<string>()
  const out: WikidataAwardWinner[] = []
  for (const r of data.results.bindings) {
    const qid = r.work.value.split('/').pop() || ''
    if (!qid || seen.has(qid)) continue
    seen.add(qid)
    const tmdb = r.tmdb?.value || null
    const tmdbTv = r.tmdbTv?.value || null
    out.push({
      qid,
      label: r.workLabel?.value || qid,
      tmdbId: tmdb || tmdbTv || null,
      type: tmdb ? 'movie' : tmdbTv ? 'tv' : null,
      year: r.year?.value ? Number(r.year.value) : null,
    })
  }
  return out
}

/**
 * Parse a Wikidata "Point(lon lat)" coordinate string into [lat, lon].
 * Returns null on malformed input.
 */
export function parseCoords(coords: string | null | undefined): [number, number] | null {
  if (!coords) return null
  const m = /^Point\(([-\d.]+)\s+([-\d.]+)\)$/.exec(coords.trim())
  if (!m) return null
  const lon = Number(m[1])
  const lat = Number(m[2])
  if (!Number.isFinite(lon) || !Number.isFinite(lat)) return null
  return [lat, lon]
}
