// Wikidata client for artist images.
// Flow: MusicBrainz artist MBID → Wikidata ID (via MB) → Image (P18 property).
//
// Wikidata API: https://www.wikidata.org/w/api.php
// Property P18 = "image" (the main image for an entity).

import { requestJson } from './http.js';

interface WikidataEntity {
  id: string;
  labels?: Record<string, { value: string }>;
  claims?: Record<string, Array<{
    mainsnak?: {
      datavalue?: { value: string };
    };
  }>>;
}

interface WikidataSearchResponse {
  search?: Array<{
    id: string;
    label: string;
    description: string;
  }>;
}

interface WikidataEntitiesResponse {
  entities?: Record<string, WikidataEntity>;
}

export class WikidataClient {
  private readonly baseUrl = 'https://www.wikidata.org/w/api.php';
  private readonly userAgent = 'muzick/0.1 (https://github.com/user/muzick)';

  /**
   * Get Wikidata ID for a MusicBrainz artist MBID.
   * Queries Wikidata for entities with the MusicBrainz artist ID (P434).
   */
  async getWikidataIdFromMbid(mbid: string): Promise<string | null> {
    // Search Wikidata for entities with this MBID (P434 = MusicBrainz artist ID)
    const url = `${this.baseUrl}?action=wbsearchentities&search=${encodeURIComponent(mbid)}&language=en&format=json&type=item&props=claims&sitefilter=musicbrainz`;
    
    try {
      const data = await requestJson<WikidataSearchResponse>(url, {
        userAgent: this.userAgent,
        minIntervalMs: 500, // Wikidata allows higher rate
      });
      
      const results = data.search ?? [];
      if (results.length > 0) {
        return results[0].id;
      }
    } catch (err) {
      console.warn('[Wikidata] getWikidataIdFromMbid failed:', (err as Error).message);
    }
    return null;
  }

  /**
   * Search Wikidata for an artist by name.
   * Less reliable than MBID → Wikidata, but works as fallback.
   */
  async searchArtistByName(name: string): Promise<string | null> {
    const url = `${this.baseUrl}?action=wbsearchentities&search=${encodeURIComponent(name)}&language=en&format=json&type=item&limit=5`;
    
    try {
      const data = await requestJson<WikidataSearchResponse>(url, {
        userAgent: this.userAgent,
        minIntervalMs: 500,
      });
      
      const results = data.search ?? [];
      // Filter for music artists (instance of: human, musical artist, band, etc.)
      for (const result of results) {
        if (this.looksLikeMusicArtist(result)) {
          return result.id;
        }
      }
      return results[0]?.id ?? null;
    } catch (err) {
      console.warn('[Wikidata] searchArtistByName failed:', (err as Error).message);
    }
    return null;
  }

  private looksLikeMusicArtist(result: { label: string; description: string }): boolean {
    const desc = result.description.toLowerCase();
    const musicKeywords = ['singer', 'musician', 'band', 'artist', 'rapper', 'producer', 'composer', 'dj', 'group'];
    return musicKeywords.some(k => desc.includes(k));
  }

  /**
   * Get the main image (P18) for a Wikidata entity.
   * Returns the image filename (e.g., "Artist_Name.jpg") which can be used
   * with Wikimedia Commons URL: https://commons.wikimedia.org/wiki/File:...
   */
  async getImage(wikidataId: string): Promise<string | null> {
    const url = `${this.baseUrl}?action=wbgetentities&ids=${encodeURIComponent(wikidataId)}&props=claims&format=json`;
    
    try {
      const data = await requestJson<WikidataEntitiesResponse>(url, {
        userAgent: this.userAgent,
        minIntervalMs: 500,
      });
      
      const entity = data.entities?.[wikidataId];
      if (!entity?.claims?.P18) return null;
      
      const imageClaim = entity.claims.P18[0];
      const imageName = imageClaim?.mainsnak?.datavalue?.value;
      if (!imageName) return null;
      
      // Return Wikimedia Commons URL
      // Format: https://commons.wikimedia.org/wiki/Special:FilePath/filename
      const encoded = encodeURIComponent(imageName.replace(/ /g, '_'));
      return `https://commons.wikimedia.org/wiki/Special:FilePath/${encoded}`;
    } catch (err) {
      console.warn('[Wikidata] getImage failed:', (err as Error).message);
    }
    return null;
  }

  /**
   * Full pipeline: MBID → Wikidata ID → Image URL.
   */
  async getArtistImageFromMbid(mbid: string): Promise<string | null> {
    const wikidataId = await this.getWikidataIdFromMbid(mbid);
    if (!wikidataId) return null;
    return this.getImage(wikidataId);
  }

  /**
   * Full pipeline fallback: name → Wikidata ID → Image URL.
   */
  async getArtistImageFromName(name: string): Promise<string | null> {
    const wikidataId = await this.searchArtistByName(name);
    if (!wikidataId) return null;
    return this.getImage(wikidataId);
  }
}