<?php

namespace App\Repositories\Admin;

use App\Models\Category;
use App\Models\Config;
use App\Models\Country;
use App\Models\Genre;
use App\Models\Movie;
use App\Models\MovieLanguage;
use App\Models\Star;
use App\Models\TvShow;
use App\Models\VideoQuality;
use App\Repositories\Interfaces\Admin\ImporterInterface;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;
use Tmdb\Laravel\Facades\Tmdb;

class ImporterRepository implements ImporterInterface
{
    /**
     * Fetch movie details from TMDB API.
     * Returns null on failure or invalid response (with API error handling).
     */
    public function getMovieDetails($movie_id, $data = null)
    {
        $apiKey = Config::where('title', 'tmdb_api_key')->value('value');
        if (empty($apiKey)) {
            return null;
        }
        // Include external_ids to get real IMDb ID (not TMDB id)
        $url = 'https://api.themoviedb.org/3/movie/'.$movie_id."?api_key={$apiKey}&append_to_response=credits,release_dates,external_ids";
        $response = Http::timeout(60)->get($url);
        if (! $response->successful()) {
            return null;
        }
        $movieDetails = $response->json();
        if (empty($movieDetails) || isset($movieDetails['success']) && $movieDetails['success'] === false) {
            return null;
        }
        if ($data) {
            return $movieDetails[$data] ?? null;
        }

        return $movieDetails;
    }

    /**
     * Fetch TV show details from TMDB API.
     * Returns null on failure or invalid response (with API error handling).
     */
    public function getTvShowDetails($show_id, $data = null)
    {
        $apiKey = Config::where('title', 'tmdb_api_key')->value('value');
        if (empty($apiKey)) {
            return null;
        }
        $url = 'https://api.themoviedb.org/3/tv/'.$show_id."?api_key={$apiKey}&append_to_response=credits,release_dates";
        $response = Http::timeout(60)->get($url);
        if (! $response->successful()) {
            return null;
        }
        $movieDetails = $response->json();
        if (empty($movieDetails) || isset($movieDetails['success']) && $movieDetails['success'] === false) {
            return null;
        }
        if ($data) {
            return $movieDetails[$data] ?? null;
        }

        return $movieDetails;
    }

    /**
     * Import a single movie or TV show from TMDB by ID.
     * Returns true on success, false on failure or unknown type.
     */
    public function importFromTMDB($language, $type, $id)
    {
        $movieTypes = [
            'movies_by_title',
            'popular_movies',
            'top_rated_movies',
            'upcoming_movies',
            'movies_by_year',
            'movies_by_genre',
            'movies_by_keyword',
            'movies_by_people',
        ];
        $tvTypes = [
            'tvshows_by_title',
            'popular_tvshows',
            'top_rated_tvshows',
            'on_air_tvshows',
            'tvshows_by_year',
            'tvshows_latest',
            'tvshows_by_people',
        ];

        if (in_array($type, $movieTypes)) {
            $details = $this->getMovieDetails($id);
            if (! $details || empty($details['id'])) {
                return false;
            }
            $genres_ids = $this->insertGenres($details['genres'] ?? []);
            $country_ids = $this->insertCountries($details['production_countries'] ?? []);
            $languages_ids = $this->insertLanguages($details['spoken_languages'] ?? []);
            $actor_ids = $this->insertActors($details['credits']['cast'] ?? []);
            $director_ids = $this->insertDirectors($details['credits']['crew'] ?? []);
            $writer_ids = $this->insertWriters($details['credits']['crew'] ?? []);
            $trailers = '';
            $formatted_keywords = '';
            // Use same $details to avoid a second API call
            $formatted_response = $this->TMDBDataFormatForSingleData($details, 'movie');
            $this->storeMovieToDBFromTMDB(
                $formatted_response,
                'movie',
                $actor_ids,
                $director_ids,
                $writer_ids,
                $country_ids,
                $genres_ids,
                $languages_ids,
                $trailers,
                $formatted_keywords
            );

            return true;
        }

        if (in_array($type, $tvTypes)) {
            $details = $this->getTvShowDetails($id);
            if (! $details || empty($details['id'])) {
                return false;
            }
            $genres_ids = $this->insertGenres($details['genres'] ?? []);
            $country_ids = $this->insertCountries($details['production_countries'] ?? []);
            $languages_ids = $this->insertLanguages($details['spoken_languages'] ?? []);
            $actor_ids = $this->insertActors($details['credits']['cast'] ?? []);
            $director_ids = $this->insertDirectors($details['credits']['crew'] ?? []);
            $writer_ids = $this->insertWriters($details['credits']['crew'] ?? []);
            $trailers = '';
            $formatted_keywords = '';
            $formatted_response = $this->TMDBDataFormatForSingleData($details, 'tv_show');
            $this->storeMovieToDBFromTMDB(
                $formatted_response,
                'tv_show',
                $actor_ids,
                $director_ids,
                $writer_ids,
                $country_ids,
                $genres_ids,
                $languages_ids,
                $trailers,
                $formatted_keywords
            );

            return true;
        }

        return false;
    }

    private function insertGenres($genres)
    {
        $ids = [];
        foreach ($genres as $value) {
            $genre = Genre::where('name', $value['name'])->first();
            if (! $genre) {
                $genre = new Genre;
                $genre->name = $value['name'];
                $genre->slug = Str::slug($value['name']);
                $genre->save();
                $ids[] = json_encode($genre->id);
            } else {
                $ids[] = json_encode($genre->id);
            }
        }

        return $ids;
    }

    private function insertCountries($contries)
    {
        $ids = [];
        foreach ($contries as $value) {
            $country = Country::where('name', $value['name'])->first();
            if (! $country) {
                $country = new Country;
                $country->name = $value['name'];
                $country->slug = Str::slug($value['name']);
                $country->save();
                $ids[] = json_encode($country->id);
            } else {
                $ids[] = json_encode($country->id);
            }
        }

        return $ids;
    }

    private function insertLanguages($languages)
    {
        $ids = [];
        foreach ($languages as $value) {
            $language = MovieLanguage::where('name', $value['english_name'])->first();
            if (! $language) {
                $language = new MovieLanguage;
                $language->name = $value['english_name'];
                $language->slug = Str::slug($value['english_name']);
                $language->save();
                $ids[] = json_encode($language->id);
            } else {
                $ids[] = json_encode($language->id);
            }
        }

        return $ids;
    }

    private function insertActors($casts)
    {
        $ids = [];
        foreach ($casts as $value) {
            if ($value['known_for_department'] == 'Acting') {
                $star = Star::where('star_type', 'actor')->where('star_name', $value['name'])->first();
                if (! $star) {
                    $star = new Star;
                    $star->star_name = $value['name'];
                    $star->slug = Str::slug($value['name']);
                    $star->star_type = $value['known_for_department'] == 'Acting' ? 'actor' : '';
                    $star->star_image = json_encode(['original_image' => TMDBImageUrl($value['profile_path'], 'w154')]);
                    $star->save();
                    $ids[] = json_encode($star->id);
                } else {
                    $ids[] = json_encode($star?->id);
                }
            }
        }

        return $ids;
    }

    private function insertDirectors($casts)
    {
        $ids = [];
        foreach ($casts as $value) {
            if ($value['known_for_department'] == 'Directing' || $value['known_for_department'] == 'Art') {
                $star = Star::where('star_type', 'director')->where('star_name', $value['name'])->first();
                if (! $star) {
                    $star = new Star;
                    $star->star_name = $value['name'];
                    $star->slug = Str::slug($value['name']);
                    $star->star_type = $value['known_for_department'] == 'Directing' ? 'director' : ($value['known_for_department'] == 'Art' ? 'director' : '');
                    $star->star_image = json_encode(['original_image' => TMDBImageUrl($value['profile_path'], 'w154')]);
                    $star->save();
                    $ids[] = json_encode($star->id);
                } else {
                    $ids[] = json_encode($star?->id);
                }
            }
        }

        return $ids;
    }

    /**
     * Writers: store image in same format as actors/directors (TMDB base URL + size).
     */
    private function insertWriters($casts)
    {
        $ids = [];
        foreach ($casts as $value) {
            if ($value['known_for_department'] == 'Writing') {
                $star = Star::where('star_type', 'writer')->where('star_name', $value['name'])->first();
                if (! $star) {
                    $star = new Star;
                    $star->star_name = $value['name'];
                    $star->slug = Str::slug($value['name']);
                    $star->star_type = $value['known_for_department'] == 'Writing' ? 'writer' : '';
                    $star->star_image = json_encode(['original_image' => TMDBImageUrl($value['profile_path'] ?? null, 'w154')]);
                    $star->save();
                    $ids[] = json_encode($star->id);
                } else {
                    $ids[] = json_encode($star?->id);
                }
            }
        }

        return $ids;
    }

    /**
     * Store movie/TV show in DB. Skips if already imported (duplicate tmdb_id).
     * Uses unique slug (name + tmdb_id), real IMDb id when available, safe runtime for TV.
     */
    private function storeMovieToDBFromTMDB(
        $movie_response,
        $type,
        $actor_ids,
        $director_ids,
        $writer_ids,
        $country_ids,
        $genres_ids,
        $languages_ids,
        $trailers,
        $formatted_keywords = ''
    ) {
        $tmdbId = $movie_response['id'] ?? null;
        if (! $tmdbId) {
            return;
        }
        // Avoid duplicate imports: check by tmdb_id
        if ($type == 'tv_show') {
            if (TvShow::where('tmdb_id', $tmdbId)->exists()) {
                throw new \Exception(__('already_imported'));
            }
            $video = new TvShow;
        } else {
            if (Movie::where('tmdb_id', $tmdbId)->exists()) {
                throw new \Exception(__('already_imported'));
            }
            $video = new Movie;
        }

        $video->tmdb_id = $tmdbId;
        // Use real IMDb id from external_ids (movie) or leave null; do not use TMDB id as imdbid
        $video->imdbid = $movie_response['imdb_id'] ?? null;
        $video->title = $movie_response['name'] ?? '';
        $video->slug = Str::slug($movie_response['name'] ?? 'item').'-'.$tmdbId;
        $video->description = $movie_response['overview'] ?? '';
        $video->stars = json_encode($actor_ids);
        $video->director = json_encode($director_ids);
        $video->writer = json_encode($writer_ids);
        $video->rating = $movie_response['imdb_rating'] ?? null;
        $video->release = $movie_response['release_date'] ?? null;
        $video->country = json_encode($country_ids);
        $video->genre = json_encode($genres_ids);
        $video->language = json_encode($languages_ids);
        // TV shows often have no top-level runtime; default to 0 to avoid invalid value
        $video->runtime = isset($movie_response['runtime']) ? (int) $movie_response['runtime'] : ($type === 'tv_show' ? 0 : null);
        $video->imdb_rating = $movie_response['vote_average'] ?? null;
        $video->video_type = Category::first()->id;
        $video->video_quality = VideoQuality::first()->id;

        $video->status = 1;
        $video->enable_download = 1;
        $video->is_free = 1;
        $video->is_tvseries = $type == 'tv_show' ? 1 : 0;

        $trailer_url = $type == 'movie'
            ? (isset($trailers['youtube'][0]['source']) ? 'https://www.youtube.com/watch?v='.$trailers['youtube'][0]['source'] : null)
            : (isset($trailers['results'][0]['key']) ? 'https://www.youtube.com/watch?v='.$trailers['results'][0]['key'] : null);

        $video->trailler_youtube_source = $trailer_url;
        $video->trailer = $trailer_url != '' ? 1 : 0;
        $video->seo_title = $movie_response['name'] ?? '';
        $video->meta_description = $movie_response['overview'] ?? '';
        $video->focus_keyword = $formatted_keywords;
        $video->tags = $formatted_keywords;
        $video->thumbnail = json_encode(['original_image' => TMDBImageUrl($movie_response['poster_path'] ?? null, 'w342')]);
        $video->poster = json_encode(['original_image' => TMDBImageUrl($movie_response['backdrop_path'] ?? null, 'w500')]);
        if ($video instanceof TvShow) {
            $video->rating = $video->imdb_rating;
        }
        $video->save();
    }

    /**
     * Normalize TMDB response for storage. Movie: imdb_id from external_ids.
     */
    private function TMDBDataFormatForSingleData($response, $type = 'movie')
    {
        if ($type == 'tv_show') {
            return [
                'id' => $response['id'] ?? null,
                'name' => $response['name'] ?? '',
                'origin_country' => $response['origin_country'] ?? [],
                'original_language' => $response['original_language'] ?? '',
                'original_name' => $response['original_name'] ?? '',
                'overview' => $response['overview'] ?? '',
                'popularity' => $response['popularity'] ?? 0,
                'release_date' => $response['first_air_date'] ?? null,
                'vote_average' => $response['vote_average'] ?? null,
                'vote_count' => $response['vote_count'] ?? 0,
                'backdrop_path' => $response['backdrop_path'] ?? null,
                'poster_path' => $response['poster_path'] ?? null,
                'runtime' => $response['runtime'] ?? 0,
                'type' => 'tv_show',
            ];
        }
        if ($type == 'movie') {
            $imdbId = $response['external_ids']['imdb_id'] ?? $response['imdb_id'] ?? null;

            return [
                'id' => $response['id'] ?? null,
                'name' => $response['title'] ?? '',
                'imdb_id' => $imdbId,
                'origin_country' => $response['original_language'] ?? '',
                'original_language' => $response['original_language'] ?? '',
                'original_name' => $response['original_title'] ?? '',
                'overview' => $response['overview'] ?? '',
                'popularity' => $response['popularity'] ?? 0,
                'release_date' => $response['release_date'] ?? null,
                'vote_average' => $response['vote_average'] ?? null,
                'vote_count' => $response['vote_count'] ?? 0,
                'backdrop_path' => $response['backdrop_path'] ?? null,
                'poster_path' => $response['poster_path'] ?? null,
                'runtime' => $response['runtime'] ?? null,
                'type' => 'movie',
            ];
        }

        return [];
    }

    /**
     * Map app language slug (e.g. bengali, hindi) to TMDB ISO 639-1 code (e.g. bn, hi).
     * If already 2 chars, assume it is ISO and return as-is.
     */
    private function slugToTmdbIsoCode(?string $slug): string
    {
        if (empty($slug)) {
            return 'en';
        }
        if (strlen($slug) === 2) {
            return strtolower($slug);
        }
        $map = [
            'english' => 'en',
            'spanish' => 'es',
            'hindi' => 'hi',
            'bengali' => 'bn',
            'french' => 'fr',
            'german' => 'de',
            'italian' => 'it',
            'japanese' => 'ja',
            'korean' => 'ko',
            'mandarin' => 'zh',
            'portuguese' => 'pt',
            'russian' => 'ru',
            'arabic' => 'ar',
            'turkish' => 'tr',
            'dutch' => 'nl',
            'vietnamese' => 'vi',
            'thai' => 'th',
            'indonesian' => 'id',
            'tamil' => 'ta',
            'telugu' => 'te',
        ];

        return $map[strtolower($slug)] ?? 'en';
    }

    /**
     * Fetch list from TMDB (search/discover/person credits).
     * "By people" flow: search person by name, then return that person's movie_credits or tv_credits so user can import by movie/TV id.
     */
    public function getTMDBData($request)
    {
        $apiKey = Config::where('title', 'tmdb_api_key')->value('value');
        if (empty($apiKey)) {
            return ['results' => [], 'total_pages' => 0, 'error' => __('tmdb_api_key_not_configured')];
        }

        $language = $this->slugToTmdbIsoCode($request['language'] ?? 'en');
        $type = $request['type'];
        $query = $request['title'] ?? null;
        $tmdb_id = $request['tmdb_id'] ?? null;
        $year = $request['year'] ?? null;
        $page = (int) ($request['page'] ?? 1);

        $params = [
            'api_key' => $apiKey,
            'language' => $language,
            'page' => $page,
            'query' => $query,
        ];

        switch ($type) {
            case 'movies_by_title':
            case 'tvshows_by_title':
                $url = $type === 'movies_by_title' ? 'https://api.themoviedb.org/3/search/movie' : 'https://api.themoviedb.org/3/search/tv';
                $params['query'] = $query;
                break;

            case 'movies_by_year':
                $url = 'https://api.themoviedb.org/3/discover/movie';
                $params['year'] = $year;
                break;

            case 'tvshows_by_year':
                $url = 'https://api.themoviedb.org/3/discover/tv';
                $params['year'] = $year;
                break;

            case 'movies_by_genre':
                $url = 'https://api.themoviedb.org/3/discover/movie';
                $params['with_genres'] = $tmdb_id;
                break;

            case 'movies_by_keyword':
                $url = 'https://api.themoviedb.org/3/search/movie';
                $params['query'] = $query;
                break;

            case 'movies_by_people':
                return $this->getMovieCreditsByPersonName($apiKey, $language, $query);

            case 'tvshows_by_people':
                return $this->getTvCreditsByPersonName($apiKey, $language, $query);

            case 'popular_movies':
            case 'top_rated_movies':
            case 'upcoming_movies':
                $endpoint = str_replace('_movies', '', $type);
                $url = "https://api.themoviedb.org/3/movie/$endpoint";
                break;

            case 'tvshows_latest':
                $url = 'https://api.themoviedb.org/3/tv/popular';
                break;

            case 'popular_tvshows':
            case 'top_rated_tvshows':
                $endpoint = str_replace('_tvshows', '', $type);
                $url = "https://api.themoviedb.org/3/tv/$endpoint";
                break;
            case 'on_air_tvshows':
                $url = 'https://api.themoviedb.org/3/tv/on_the_air';
                break;

            default:
                return ['results' => [], 'total_pages' => 0, 'error' => __('invalid_search_type')];
        }

        $response = Http::timeout(60)->get($url, $params);
        $data = $response->json();
        if (! $response->successful() || ! is_array($data)) {
            return ['results' => [], 'total_pages' => 0, 'error' => __('error_fetching_from_tmdb')];
        }

        return $data;
    }

    /**
     * Search person by name, then return their movie credits so list shows movies (import by movie id).
     */
    private function getMovieCreditsByPersonName($apiKey, $language, $query)
    {
        if (empty(trim($query ?? ''))) {
            return ['results' => [], 'total_pages' => 1, 'error' => __('enter_person_name_to_search')];
        }
        $searchUrl = 'https://api.themoviedb.org/3/search/person';
        $searchResponse = Http::timeout(60)->get($searchUrl, ['api_key' => $apiKey, 'language' => $language, 'query' => $query]);
        $searchData = $searchResponse->json();
        $persons = $searchData['results'] ?? [];
        if (empty($persons)) {
            return ['results' => [], 'total_pages' => 1];
        }
        $personId = $persons[0]['id'];
        $creditsUrl = "https://api.themoviedb.org/3/person/{$personId}/movie_credits";
        $creditsResponse = Http::timeout(60)->get($creditsUrl, ['api_key' => $apiKey, 'language' => $language]);
        $creditsData = $creditsResponse->json();
        $cast = $creditsData['cast'] ?? [];

        return ['results' => $cast, 'total_pages' => 1];
    }

    /**
     * Search person by name, then return their TV credits so list shows TV shows (import by TV id).
     */
    private function getTvCreditsByPersonName($apiKey, $language, $query)
    {
        if (empty(trim($query ?? ''))) {
            return ['results' => [], 'total_pages' => 1, 'error' => __('enter_person_name_to_search')];
        }
        $searchUrl = 'https://api.themoviedb.org/3/search/person';
        $searchResponse = Http::timeout(60)->get($searchUrl, ['api_key' => $apiKey, 'language' => $language, 'query' => $query]);
        $searchData = $searchResponse->json();
        $persons = $searchData['results'] ?? [];
        if (empty($persons)) {
            return ['results' => [], 'total_pages' => 1];
        }
        $personId = $persons[0]['id'];
        $creditsUrl = "https://api.themoviedb.org/3/person/{$personId}/tv_credits";
        $creditsResponse = Http::timeout(60)->get($creditsUrl, ['api_key' => $apiKey, 'language' => $language]);
        $creditsData = $creditsResponse->json();
        $cast = $creditsData['cast'] ?? [];

        return ['results' => $cast, 'total_pages' => 1];
    }

    /**
     * Fetch TMDB movie genre list for dropdown (id => name).
     */
    public function getTmdbGenres()
    {
        $apiKey = Config::where('title', 'tmdb_api_key')->value('value');
        if (empty($apiKey)) {
            return [];
        }
        $response = Http::timeout(15)->get('https://api.themoviedb.org/3/genre/movie/list', ['api_key' => $apiKey]);
        $data = $response->json();

        return $data['genres'] ?? [];
    }
}
