/// A verified external reference for a species, built deterministically from the
/// bundled catalog's identifiers — never hand-entered. These are read-only
/// "learn more" pointers, distinct from the links a grower adds by hand.
enum ReferenceSource { gbif, wikipedia, wikispecies }

/// One reference pointer: which source it is (for the label/icon) and the URL.
class ReferenceLink {
  const ReferenceLink({required this.source, required this.url});

  final ReferenceSource source;
  final String url;

  @override
  bool operator ==(Object other) =>
      other is ReferenceLink && other.source == source && other.url == url;

  @override
  int get hashCode => Object.hash(source, url);

  @override
  String toString() => 'ReferenceLink($source, $url)';
}

/// Builds the verified reference links for a species from its bundled
/// identifiers. Deterministic and offline — no titles are bundled and no
/// network call is made; each URL is derived from a stable identifier:
///
/// * **GBIF** — the taxon page for the backbone [gbifKey].
/// * **Wikipedia** — locale-aware via Wikidata's `Special:GoToLinkedPage`
///   redirect, so we need only the [wikidataQid] and the user's [languageCode]:
///   the redirect resolves to the article in that language, falling back to the
///   Wikidata item when the language has no article. Works under RTL locales.
/// * **Wikispecies** — the canonical article keyed by [scientificName].
///
/// A source is only included when its required identifier is present, so a
/// species missing (say) a GBIF key simply yields fewer links.
List<ReferenceLink> speciesReferenceLinks({
  required String scientificName,
  int? gbifKey,
  String? wikidataQid,
  String languageCode = 'en',
}) {
  final links = <ReferenceLink>[];

  if (gbifKey != null) {
    links.add(
      ReferenceLink(
        source: ReferenceSource.gbif,
        url: 'https://www.gbif.org/species/$gbifKey',
      ),
    );
  }

  final qid = wikidataQid?.trim();
  if (qid != null && qid.isNotEmpty) {
    final lang = _wikiLang(languageCode);
    links.add(
      ReferenceLink(
        source: ReferenceSource.wikipedia,
        url:
            'https://www.wikidata.org/wiki/Special:GoToLinkedPage/${lang}wiki/$qid',
      ),
    );
  }

  final title = scientificName.trim();
  if (title.isNotEmpty) {
    links.add(
      ReferenceLink(
        source: ReferenceSource.wikispecies,
        url: 'https://species.wikimedia.org/wiki/${_wikiTitle(title)}',
      ),
    );
  }

  return links;
}

/// The bare Wikipedia language subtag: drops any region (`pt_BR` → `pt`) and
/// falls back to English for an empty/blank locale.
String _wikiLang(String languageCode) {
  final base = languageCode.split(RegExp('[-_]')).first.trim().toLowerCase();
  return base.isEmpty ? 'en' : base;
}

/// A MediaWiki page title: spaces become underscores, then non-ASCII is
/// percent-encoded (scientific names are Latin, but stay safe for hybrids and
/// diacritics). `_` is left intact by [Uri.encodeFull].
String _wikiTitle(String scientificName) =>
    Uri.encodeFull(scientificName.replaceAll(' ', '_'));
