<?php

namespace App\Services;

use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;

/**
 * Custom iTunes Search API Service.
 * Replaces the DariusIII/php-itunes-api package with a direct implementation.
 * Supports music and ebook searches only (movies are not supported due to API issues).
 *
 * @see https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/iTuneSearchAPI/index.html
 */
class ItunesService
{
    protected const API_URL = 'https://itunes.apple.com/search';

    protected const LOOKUP_URL = 'https://itunes.apple.com/lookup';

    protected Client $client;

    protected int $limit = 25;

    protected string $country = 'US';

    public function __construct()
    {
        $this->client = new Client([
            'timeout' => 15,
            'connect_timeout' => 10,
            'http_errors' => false,
            'headers' => [
                'Accept' => 'application/json',
                'User-Agent' => 'NNTmux/1.0',
            ],
        ]);
    }

    /**
     * Set the result limit.
     */
    public function limit(int $limit): self
    {
        $this->limit = min($limit, 200); // iTunes max is 200

        return $this;
    }

    /**
     * Set the country code for the search.
     */
    public function country(string $country): self
    {
        $this->country = strtoupper($country);

        return $this;
    }

    /**
     * Search for music albums.
     */
    public function searchAlbums(string $term): ?array
    {
        return $this->search($term, 'album', 'music');
    }

    /**
     * Search for music tracks.
     */
    public function searchTracks(string $term): ?array
    {
        return $this->search($term, 'song', 'music');
    }

    /**
     * Search for artists.
     */
    public function searchArtists(string $term): ?array
    {
        return $this->search($term, 'musicArtist', 'music');
    }

    /**
     * Search for ebooks.
     */
    public function searchEbooks(string $term): ?array
    {
        return $this->search($term, 'ebook', 'ebook');
    }

    /**
     * Search for audiobooks.
     */
    public function searchAudiobooks(string $term): ?array
    {
        return $this->search($term, 'audiobook', 'audiobook');
    }

    /**
     * Lookup by iTunes ID.
     */
    public function lookupById(int $id): ?array
    {
        $cacheKey = 'itunes_lookup_'.$id;
        $cached = Cache::get($cacheKey);

        if ($cached !== null) {
            return $cached;
        }

        try {
            $response = $this->client->get(self::LOOKUP_URL, [
                'query' => [
                    'id' => $id,
                    'country' => $this->country,
                ],
            ]);

            $statusCode = $response->getStatusCode();
            if ($statusCode !== 200) {
                Log::warning("iTunes API lookup returned status {$statusCode}");

                return null;
            }

            $data = json_decode($response->getBody()->getContents(), true);

            if (! isset($data['results']) || empty($data['results'])) {
                return null;
            }

            Cache::put($cacheKey, $data['results'][0], now()->addDays(7));

            return $data['results'][0];
        } catch (GuzzleException $e) {
            Log::error('iTunes API lookup error: '.$e->getMessage());

            return null;
        }
    }

    /**
     * Lookup artist by ID.
     */
    public function lookupArtist(int $artistId): ?array
    {
        return $this->lookupById($artistId);
    }

    /**
     * Get the first album result for a search term.
     *
     * @return array|null Normalized album data or null
     */
    public function findAlbum(string $term): ?array
    {
        $results = $this->searchAlbums($term);

        if (empty($results)) {
            return null;
        }

        return $this->normalizeAlbumResult($results[0]);
    }

    /**
     * Get the first track result for a search term.
     *
     * @return array|null Normalized track data or null
     */
    public function findTrack(string $term): ?array
    {
        $results = $this->searchTracks($term);

        if (empty($results)) {
            return null;
        }

        return $this->normalizeTrackResult($results[0]);
    }

    /**
     * Get the first ebook result for a search term.
     *
     * @return array|null Normalized ebook data or null
     */
    public function findEbook(string $term): ?array
    {
        $results = $this->searchEbooks($term);

        if (empty($results)) {
            return null;
        }

        return $this->normalizeEbookResult($results[0]);
    }

    /**
     * Perform a search request to iTunes API.
     */
    protected function search(string $term, string $entity, string $media): ?array
    {
        $term = trim($term);
        if (empty($term)) {
            return null;
        }

        $cacheKey = 'itunes_search_'.md5($term.$entity.$media.$this->country.$this->limit);
        $cached = Cache::get($cacheKey);

        if ($cached !== null) {
            return $cached;
        }

        try {
            $response = $this->client->get(self::API_URL, [
                'query' => [
                    'term' => $term,
                    'entity' => $entity,
                    'media' => $media,
                    'country' => $this->country,
                    'limit' => $this->limit,
                ],
            ]);

            $statusCode = $response->getStatusCode();
            if ($statusCode !== 200) {
                Log::warning("iTunes API search returned status {$statusCode}");

                return null;
            }

            $data = json_decode($response->getBody()->getContents(), true);

            if (! isset($data['results'])) {
                return null;
            }

            $results = $data['results'];
            Cache::put($cacheKey, $results, now()->addHours(6));

            return $results;
        } catch (GuzzleException $e) {
            Log::error('iTunes API search error: '.$e->getMessage());

            return null;
        }
    }

    /**
     * Normalize album result to consistent format.
     */
    protected function normalizeAlbumResult(array $result): array
    {
        return [
            'id' => $result['collectionId'] ?? null,
            'name' => $result['collectionName'] ?? '',
            'artist' => $result['artistName'] ?? '',
            'artist_id' => $result['artistId'] ?? null,
            'genre' => $result['primaryGenreName'] ?? '',
            'release_date' => isset($result['releaseDate']) ? $this->parseDate($result['releaseDate']) : null,
            'track_count' => $result['trackCount'] ?? 0,
            'cover' => $this->getHighResCover($result['artworkUrl100'] ?? ''),
            'store_url' => $result['collectionViewUrl'] ?? '',
            'price' => $result['collectionPrice'] ?? null,
            'currency' => $result['currency'] ?? 'USD',
            'copyright' => $result['copyright'] ?? '',
            'explicit' => ($result['collectionExplicitness'] ?? '') === 'explicit',
            'raw' => $result,
        ];
    }

    /**
     * Normalize track result to consistent format.
     */
    protected function normalizeTrackResult(array $result): array
    {
        return [
            'id' => $result['trackId'] ?? null,
            'name' => $result['trackName'] ?? '',
            'artist' => $result['artistName'] ?? '',
            'artist_id' => $result['artistId'] ?? null,
            'album' => $result['collectionName'] ?? '',
            'album_id' => $result['collectionId'] ?? null,
            'genre' => $result['primaryGenreName'] ?? '',
            'release_date' => isset($result['releaseDate']) ? $this->parseDate($result['releaseDate']) : null,
            'duration_ms' => $result['trackTimeMillis'] ?? 0,
            'cover' => $this->getHighResCover($result['artworkUrl100'] ?? ''),
            'preview_url' => $result['previewUrl'] ?? '',
            'store_url' => $result['trackViewUrl'] ?? '',
            'price' => $result['trackPrice'] ?? null,
            'currency' => $result['currency'] ?? 'USD',
            'explicit' => ($result['trackExplicitness'] ?? '') === 'explicit',
            'raw' => $result,
        ];
    }

    /**
     * Normalize ebook result to consistent format.
     */
    protected function normalizeEbookResult(array $result): array
    {
        return [
            'id' => $result['trackId'] ?? null,
            'name' => $result['trackName'] ?? '',
            'author' => $result['artistName'] ?? '',
            'author_id' => $result['artistId'] ?? null,
            'genres' => $result['genres'] ?? [],
            'genre' => $result['primaryGenreName'] ?? ($result['genres'][0] ?? ''),
            'release_date' => isset($result['releaseDate']) ? $this->parseDate($result['releaseDate']) : null,
            'description' => $result['description'] ?? '',
            'cover' => $this->getHighResCover($result['artworkUrl100'] ?? ''),
            'store_url' => $result['trackViewUrl'] ?? '',
            'price' => $result['price'] ?? null,
            'currency' => $result['currency'] ?? 'USD',
            'average_rating' => $result['averageUserRating'] ?? null,
            'rating_count' => $result['userRatingCount'] ?? 0,
            'raw' => $result,
        ];
    }

    /**
     * Convert iTunes 100x100 artwork URL to higher resolution.
     */
    protected function getHighResCover(string $url, int $size = 800): string
    {
        if (empty($url)) {
            return '';
        }

        // iTunes artwork URLs typically end with /100x100bb.jpg
        // We can replace it with larger sizes up to 3000x3000
        return preg_replace('/\d+x\d+(bb)?\./', $size.'x'.$size.'bb.', $url) ?? $url;
    }

    /**
     * Parse ISO 8601 date string.
     */
    protected function parseDate(string $dateString): ?string
    {
        try {
            return \Carbon\Carbon::parse($dateString)->format('Y-m-d');
        } catch (\Exception $e) {
            return null;
        }
    }
}
