// deno-lint-ignore-file no-explicit-any
/**
 * Maps transit systems to their logo in `static/icons/trains/logos/` by
 * matching the `network`/`operator` tags (and their `:wikidata` variants)
 * that process.lua now writes on railway POIs.
 *
 * Every alias below was confirmed against real OSM data by running tilemaker
 * with a debug build of process.lua over the relevant regional .osm.pbf
 * extract — not guessed from Wikidata lookups alone. Coverage is intentionally
 * partial: stations with no match, or an ambiguous one, fall back to the
 * generic station icon (see `stationIconExpression` below) rather than risk a
 * wrong logo.
 *
 * @module
 */
import type maplibregl from 'maplibre-gl'
import { all, any, cond, contains, includes } from './expr.ts'
import { loadStationIcon, TRAIN_STATION_ICON } from './map-icons.ts'

export interface StationNetworkMatch {
  networkWikidata?: string[]
  operatorWikidata?: string[]
  networkText?: string[]
  operatorText?: string[]
  /** OSM's `station=*` sub-tag (subway/light_rail/...) — disambiguates mode
   * where network/operator tags are shared across systems (e.g. Berlin's
   * U-Bahn and S-Bahn both sit under the VBB fare network). */
  stationMode?: string[]
}

export interface StationNetworkEntry {
  /** Logo slug — matches `static/icons/trains/logos/<slug>.png` */
  slug: string
  /** OR'd together; each entry's populated fields are AND'd */
  matches: StationNetworkMatch[]
}

export const STATION_NETWORKS: StationNetworkEntry[] = [
  {
    slug: 'madrid',
    matches: [{
      networkWikidata: ['Q191987'],
      networkText: ['Metro de Madrid'],
    }],
  },
  {
    // The Cercanías brand (and this logo) is shared across every Spanish
    // city's commuter-rail network, not just Madrid's — matching the plain
    // 'Cercanías' substring catches 'Cercanías Madrid', 'Cercanías
    // Barcelona', etc. all with one entry, rather than needing a separate
    // wikidata ID (and logo) per city.
    slug: 'cercanias',
    matches: [{
      networkWikidata: ['Q1054785'],
      networkText: ['Cercanías'],
    }],
  },
  {
    slug: 'paris',
    matches: [{
      networkWikidata: ['Q50716'],
      networkText: ['Métro de Paris'],
    }],
  },
  {
    slug: 'tokyo',
    matches: [{
      operatorWikidata: ['Q682894'],
      operatorText: ['東京地下鉄'],
    }],
  },
  {
    slug: 'tokyo-toei',
    matches: [{
      operatorWikidata: ['Q1377135'],
      operatorText: ['東京都交通局'],
    }],
  },
  {
    slug: 'tokyo-jr-east',
    matches: [{
      operatorWikidata: ['Q499071'],
      operatorText: ['東日本旅客鉄道'],
    }],
  },
  {
    // '北京地铁' (Beijing Subway) shows up as either `network` or `operator`
    // across different stations — real OSM data is inconsistent here.
    slug: 'beijing',
    matches: [{
      networkText: ['北京地铁'],
      operatorText: ['北京地铁', '北京轨道运营'],
    }],
  },
  {
    slug: 'beijing-mtr',
    matches: [{ operatorText: ['北京京港地铁'] }],
  },
  {
    slug: 'hongkong',
    matches: [{
      networkWikidata: ['Q14751'],
      operatorWikidata: ['Q1479375'],
      networkText: ['MTR', '港鐵 MTR'],
      operatorText: [
        '香港鐵路有限公司 MTR Corporation',
        '香港鐵路有限公司 MTR Corporation Limited',
        '港鐵公司 MTR Corporation',
      ],
    }],
  },
  {
    slug: 'berlin-u',
    matches: [{
      networkText: ['Verkehrsverbund Berlin-Brandenburg'],
      stationMode: ['subway'],
    }],
  },
  {
    slug: 'berlin-s',
    matches: [
      { networkWikidata: ['Q315451'] },
      {
        networkText: ['Verkehrsverbund Berlin-Brandenburg'],
        stationMode: ['light_rail'],
      },
    ],
  },
  {
    slug: 'new-york',
    matches: [{
      networkText: ['NYC Subway'],
      operatorText: ['New York City Transit Authority'],
    }],
  },
  {
    slug: 'new-york-path',
    matches: [{
      networkText: ['PATH'],
      operatorWikidata: ['Q908666'],
      operatorText: ['Port Authority Trans-Hudson'],
    }],
  },
  {
    // Only one Bay Area logo exists; BART is the region's rapid-transit
    // "metro" (matching the plain-cityname convention used for Madrid,
    // Beijing, Seoul, Singapore below) rather than Muni, which would need
    // its own suffixed slug the way Berlin/Tokyo's secondary systems do.
    slug: 'san-francisco',
    matches: [{
      networkText: ['BART'],
      operatorText: ['San Francisco Bay Area Rapid Transit District'],
    }],
  },
  {
    // Nationwide US network, not tied to any one region's extract.
    slug: 'amtrak',
    matches: [{
      networkWikidata: ['Q23239'],
      networkText: ['Amtrak'],
    }],
  },
  {
    slug: 'singapore',
    matches: [{
      networkWikidata: ['Q40176'],
      networkText: ['Mass Rapid Transit'],
    }],
  },
  {
    slug: 'seoul',
    matches: [{
      networkWikidata: ['Q16950'],
      networkText: ['수도권 전철'],
    }],
  },
  {
    slug: 'london-1',
    matches: [{
      networkWikidata: ['Q20075'],
      networkText: ['London Underground'],
      operatorText: ['London Underground'],
    }],
  },
  {
    slug: 'london-dlr',
    matches: [{ networkText: ['Docklands Light Railway'] }],
  },
  {
    slug: 'london-overground',
    matches: [{
      networkWikidata: ['Q746021'],
      networkText: ['London Overground'],
    }],
  },
]

function fieldCondition(prop: string, values?: string[]): any[] | null {
  if (!values || values.length === 0) return null
  return any(...values.map((v) => contains(v, prop)))
}

/**
 * network/operator wikidata and text are alternative ways of recognizing the
 * same system — a station only ever carries some subset of them, so they're
 * OR'd. `stationMode` is different: it's a mode disambiguator layered on top
 * (Berlin's U-Bahn/S-Bahn share the same network text), so it's AND'd with
 * whichever of the above are present.
 */
function matchCondition(m: StationNetworkMatch): any[] {
  const identityParts = [
    fieldCondition('network_wikidata', m.networkWikidata),
    fieldCondition('operator_wikidata', m.operatorWikidata),
    fieldCondition('network', m.networkText),
    fieldCondition('operator', m.operatorText),
  ].filter((p): p is any[] => p !== null)
  const identity = any(...identityParts)
  if (!m.stationMode) return identity
  return all(includes('station_mode', m.stationMode), identity)
}

function entryCondition(entry: StationNetworkEntry): any[] {
  return any(...entry.matches.map(matchCondition))
}

/** MapLibre icon-name for a given logo slug. */
export function stationLogoIcon(slug: string): string {
  return `station-logo-${slug}`
}

/**
 * `icon-image` expression: the first matching network's logo, falling back
 * to the generic station icon. Built as a `case` expression so a station
 * only ever resolves to one logo, in the order STATION_NETWORKS is defined.
 */
export function stationIconExpression(): any[] {
  const branches = STATION_NETWORKS.flatMap((
    entry,
  ) => [entryCondition(entry), stationLogoIcon(entry.slug)])
  return cond(...branches, TRAIN_STATION_ICON)
}

/**
 * Registers every network logo image on `map` if not already there, so the
 * `stationIconExpression()` icon-image lookups resolve. Mirrors
 * `ensureTrainStationIcon`'s load-from-file approach.
 */
export async function ensureStationLogoIcons(
  map: maplibregl.Map,
): Promise<void> {
  await Promise.all(STATION_NETWORKS.map(async (entry) => {
    const icon = stationLogoIcon(entry.slug)
    if (map.hasImage(icon)) return
    const image = await loadStationIcon(
      `/static/icons/trains/logos/${entry.slug}.png`,
    )
    if (!map.hasImage(icon)) {
      map.addImage(icon, image, { pixelRatio: 2 })
    }
  }))
}
