// iTunes Search API client (read-only).
//
// The iTunes Search API is free, requires no API key, and has excellent
// coverage — nearly every released album is present. We use the album search
// to resolve cover art URLs. The default artwork URL returned is 100×100, but
// can be upgraded to 600×600 by swapping the size suffix in the URL string.
//
// Rate limit: Apple asks for "moderate" use (~20 req/min). We enforce a polite
// per-host interval via the http base.
//
// Best-effort: on any error, methods log a warning and return null rather than
// throwing, so enrichment never breaks the worker.

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

/** A normalised iTunes album search result (only the fields we read). */
export interface ITunesAlbum {
  /** Artist canonical name. */
  artistName: string;
  /** Album / collection title. */
  collectionName: string;
  /** Cover art URL (100×100 — caller upscales). */
  artworkUrl100: string;
}

interface ITunesSearchResult {
  artistName?: string;
  collectionName?: string;
  artworkUrl100?: string;
}

interface ITunesSearchResponse {
  results?: ITunesSearchResult[];
}

export class ITunesClient {
  private readonly baseUrl = 'https://itunes.apple.com';
  private readonly userAgent: string;
  private readonly minIntervalMs: number;

  constructor(contact = '', minIntervalMs = 3000) {
    this.userAgent = buildUserAgent(contact);
    this.minIntervalMs = minIntervalMs;
  }

  /**
   * Search the iTunes catalog for an album by artist + title and return the
   * best match, or null if no match / error. The returned `artworkUrl100` is
   * the 100×100 thumbnail; callers upscale it to 600×600 via `upscaleArtwork`.
   */
  async searchAlbum(artist: string, album: string): Promise<ITunesAlbum | null> {
    if (artist.trim() === '' || album.trim() === '') return null;

    // The iTunes search `term` is a free-text query; we quote both values to
    // narrow the match. entity=album restricts to album collections.
    const term = `${artist} ${album}`;
    const qs = new URLSearchParams({ term, entity: 'album', limit: '5' });
    const url = `${this.baseUrl}/search?${qs.toString()}`;

    try {
      const data = await requestJson<ITunesSearchResponse>(url, {
        userAgent: this.userAgent,
        minIntervalMs: this.minIntervalMs,
      });
      const results = data.results ?? [];
      if (results.length === 0) return null;

      // Prefer an exact title match (case-insensitive) to avoid grabbing a
      // "Greatest Hits" when searching for a studio album, then fall back to
      // the first result.
      const lowerAlbum = album.toLowerCase();
      const exact = results.find(
        (r) => r.collectionName?.toLowerCase() === lowerAlbum
      );
      const best = exact ?? results[0];

      if (!best.artworkUrl100) return null;
      return {
        artistName: best.artistName ?? artist,
        collectionName: best.collectionName ?? album,
        artworkUrl100: best.artworkUrl100,
      };
    } catch (err) {
      console.warn('[iTunes] searchAlbum failed:', (err as Error).message);
      return null;
    }
  }
}

/**
 * Upscale an iTunes 100×100 artwork URL to a larger size. iTunes serves
 * artwork from a CDN that supports arbitrary sizes via the `NNNxNNN` path
 * segment (e.g. 600x600). Returns the input unchanged if it doesn't match the
 * expected shape.
 */
export function upscaleITunesArtwork(url: string, size = 600): string {
  return url.replace(/\/\d+x\d+(bb)?\.(jpg|png)$/, `/${size}x${size}bb.$2`);
}
