<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\Genre;
use App\Models\Episode;
use App\Models\Movie;
use App\Models\Season;
use App\Models\Show;
use App\Services\MediaStorageService;
use App\Services\ReceiverDatabaseSyncService;
use App\Services\TmdbService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Throwable;

class TmdbBulkImportController extends Controller
{
    public function index(TmdbService $tmdb)
    {
        $genres = [];
        $languages = [];

        try {
            $lists = $tmdb->genres();
            foreach (($lists['movie'] ?? []) as $genre) {
                $genres[] = ['id' => $genre['id'], 'name' => $genre['name'], 'type' => 'movie'];
            }
            foreach (($lists['tv'] ?? []) as $genre) {
                $genres[] = ['id' => $genre['id'], 'name' => $genre['name'], 'type' => 'show'];
            }
        } catch (Throwable $exception) {
            report($exception);
        }

        try {
            $languages = collect($tmdb->languages())
                ->filter(fn (array $language) => !empty($language['iso_639_1']))
                ->map(function (array $language) {
                    $englishName = trim((string) ($language['english_name'] ?? ''));
                    $nativeName = trim((string) ($language['name'] ?? ''));
                    $label = $englishName !== '' ? $englishName : ($nativeName !== '' ? $nativeName : strtoupper($language['iso_639_1']));

                    if ($nativeName !== '' && strcasecmp($nativeName, $label) !== 0) {
                        $label .= ' — ' . $nativeName;
                    }

                    return [
                        'code' => strtolower($language['iso_639_1']),
                        'label' => $label,
                    ];
                })
                ->unique('code')
                ->sortBy('label', SORT_NATURAL | SORT_FLAG_CASE)
                ->values()
                ->all();
        } catch (Throwable $exception) {
            report($exception);
        }

        return view('admin.tmdb-bulk', compact('genres', 'languages'));
    }

    public function discover(Request $request, TmdbService $tmdb): JsonResponse
    {
        $data = $request->validate([
            'kind' => ['required', 'in:movie,show'],
            'source' => ['required', 'in:new_releases,upcoming,popular,top_rated,trending,discover,random'],
            'page' => ['required', 'integer', 'min:1', 'max:500'],
            'year' => ['nullable', 'integer', 'min:1900', 'max:' . (date('Y') + 5)],
            'genre_id' => ['nullable', 'integer', 'min:1'],
            'language' => ['nullable', 'string', 'max:10'],
            'min_rating' => ['nullable', 'numeric', 'min:0', 'max:10'],
            'min_votes' => ['nullable', 'integer', 'min:0', 'max:1000000'],
        ]);

        try {
            $result = $tmdb->discoverBulk($data['kind'], $data['source'], (int) $data['page'], [
                'year' => $data['year'] ?? null,
                'genre_id' => $data['genre_id'] ?? null,
                'language' => $data['language'] ?? null,
                'min_rating' => $data['min_rating'] ?? 0,
                'min_votes' => $data['min_votes'] ?? 0,
            ]);

            $ids = collect($result['results'] ?? [])->pluck('id')->filter()->map(fn ($id) => (int) $id)->values();
            $model = $data['kind'] === 'movie' ? Movie::class : Show::class;
            $existingIds = $model::query()->whereIn('tmdb_id', $ids)->pluck('tmdb_id')->map(fn ($id) => (int) $id);
            $newIds = $ids->diff($existingIds)->values();

            return response()->json([
                'ok' => true,
                'ids' => $newIds,
                'duplicate_count' => $ids->count() - $newIds->count(),
                'page' => (int) ($result['page'] ?? $data['page']),
                'total_pages' => (int) ($result['total_pages'] ?? 1),
            ]);
        } catch (Throwable $exception) {
            report($exception);
            return response()->json(['ok' => false, 'message' => $exception->getMessage()], 422);
        }
    }

    public function importOne(Request $request, TmdbService $tmdb, MediaStorageService $media, ReceiverDatabaseSyncService $receiverSync): JsonResponse
    {
        $data = $request->validate([
            'kind' => ['required', 'in:movie,show'],
            'tmdb_id' => ['required', 'integer', 'min:1'],
        ]);

        try {
            $tmdbId = (int) $data['tmdb_id'];
            $alreadyExists = $data['kind'] === 'movie'
                ? Movie::where('tmdb_id', $tmdbId)->exists()
                : Show::where('tmdb_id', $tmdbId)->exists();

            if ($alreadyExists) {
                $response = [
                    'ok' => true,
                    'status' => 'skipped',
                    'tmdb_id' => $tmdbId,
                    'message' => 'Duplicate blocked: this TMDB title already exists.',
                ];

                // A previously imported TV show may not have seasons yet. Keep the
                // show duplicate-blocked, but allow the browser to sync its seasons.
                if ($data['kind'] === 'show') {
                    $show = Show::where('tmdb_id', $tmdbId)->firstOrFail();
                    $details = $tmdb->show($tmdbId);
                    $response['show_id'] = $show->id;
                    $response['title'] = $show->title;
                    $response['season_numbers'] = collect($details['seasons'] ?? [])
                        ->pluck('season_number')
                        ->filter(fn ($number) => $number !== null)
                        ->map(fn ($number) => (int) $number)
                        ->unique()
                        ->sort()
                        ->values()
                        ->all();
                }

                if ($data['kind'] === 'movie') {
                    $receiverSync->syncMovie(Movie::where('tmdb_id', $tmdbId)->firstOrFail());
                } else {
                    $receiverSync->syncShow(Show::where('tmdb_id', $tmdbId)->firstOrFail());
                }

                return response()->json($response);
            }

            $result = $data['kind'] === 'movie'
                ? $this->importMovie($tmdbId, $tmdb, $media)
                : $this->importShow($tmdbId, $tmdb, $media);

            if ($data['kind'] === 'movie') {
                $receiverSync->syncMovie(Movie::where('tmdb_id', $tmdbId)->firstOrFail());
            } else {
                $receiverSync->syncShow(Show::where('tmdb_id', $tmdbId)->firstOrFail());
            }

            return response()->json(['ok' => true, 'receiver_synced' => $receiverSync->enabled()] + $result);
        } catch (Throwable $exception) {
            report($exception);
            return response()->json([
                'ok' => false,
                'message' => $exception->getMessage(),
                'tmdb_id' => (int) $data['tmdb_id'],
            ], 422);
        }
    }

    public function importSeason(Request $request, TmdbService $tmdb, MediaStorageService $media, ReceiverDatabaseSyncService $receiverSync): JsonResponse
    {
        $data = $request->validate([
            'show_id' => ['required', 'integer', 'exists:shows,id'],
            'season_number' => ['required', 'integer', 'min:0', 'max:999'],
        ]);

        try {
            $show = Show::findOrFail((int) $data['show_id']);
            $payload = $tmdb->season($show->tmdb_id, (int) $data['season_number']);

            $oldSeason = Season::where('show_id', $show->id)
                ->where('season_number', (int) $payload['season_number'])
                ->first();

            $poster = $oldSeason?->poster_path;
            if (!$poster && !empty($payload['poster_path'])) {
                try {
                    $poster = $media->storeRemoteImage($payload['poster_path'], 'shows/seasons', 'w780');
                } catch (Throwable $exception) {
                    report($exception);
                }
            }

            $season = Season::updateOrCreate(
                ['show_id' => $show->id, 'season_number' => (int) $payload['season_number']],
                [
                    'tmdb_id' => $payload['id'] ?? null,
                    'title' => $payload['name'] ?? ('Season ' . $payload['season_number']),
                    'overview' => $payload['overview'] ?? '',
                    'poster_path' => $poster,
                    'air_date' => !empty($payload['air_date']) ? $payload['air_date'] : null,
                    'is_active' => true,
                ]
            );

            $episodeCount = 0;
            foreach (($payload['episodes'] ?? []) as $episodeData) {
                if (!isset($episodeData['episode_number'])) {
                    continue;
                }

                $oldEpisode = Episode::where('season_id', $season->id)
                    ->where('episode_number', (int) $episodeData['episode_number'])
                    ->first();

                $still = $oldEpisode?->still_path;
                if (!$still && !empty($episodeData['still_path'])) {
                    try {
                        $still = $media->storeRemoteImage($episodeData['still_path'], 'shows/episodes', 'w780');
                    } catch (Throwable $exception) {
                        report($exception);
                    }
                }

                Episode::updateOrCreate(
                    ['season_id' => $season->id, 'episode_number' => (int) $episodeData['episode_number']],
                    [
                        'show_id' => $show->id,
                        'tmdb_id' => $episodeData['id'] ?? null,
                        'title' => $episodeData['name'] ?? ('Episode ' . $episodeData['episode_number']),
                        'overview' => $episodeData['overview'] ?? '',
                        'still_path' => $still,
                        'air_date' => !empty($episodeData['air_date']) ? $episodeData['air_date'] : null,
                        'runtime' => $episodeData['runtime'] ?? null,
                        'vote_average' => $episodeData['vote_average'] ?? null,
                        'is_active' => true,
                    ]
                );
                $episodeCount++;
            }

            $receiverSync->syncSeason($season->fresh(['show.genres', 'episodes']));

            return response()->json([
                'ok' => true,
                'receiver_synced' => $receiverSync->enabled(),
                'show_id' => $show->id,
                'season_number' => $season->season_number,
                'season_title' => $season->title,
                'episode_count' => $episodeCount,
            ]);
        } catch (Throwable $exception) {
            report($exception);
            return response()->json([
                'ok' => false,
                'message' => $exception->getMessage(),
            ], 422);
        }
    }

    private function importMovie(int $id, TmdbService $tmdb, MediaStorageService $media): array
    {
        $data = $tmdb->movie($id);
        $old = null;

        $poster = $old?->poster_path ?: $media->storeRemoteImage($data['poster_path'] ?? null, 'movies/posters', 'w780');
        $backdrop = $old?->backdrop_path ?: $media->storeRemoteImage($data['backdrop_path'] ?? null, 'movies/backdrops', 'original');
        $trailerKey = $this->trailer($data['videos']['results'] ?? []);
        $topCast = !empty($old?->top_cast) ? $old->top_cast : $this->storeTopCast($data['credits']['cast'] ?? [], $media, 'movies/cast');

        $movie = Movie::create([
            'tmdb_id' => $data['id'],
            'imdb_id' => $data['external_ids']['imdb_id'] ?? null,
            'title' => $data['title'],
            'slug' => $old?->slug ?: $this->uniqueMovieSlug($data['title'], substr($data['release_date'] ?? '', 0, 4)),
            'overview' => $data['overview'] ?? '',
            'poster_path' => $poster,
            'backdrop_path' => $backdrop,
            'top_cast' => $topCast,
            'release_date' => !empty($data['release_date']) ? $data['release_date'] : null,
            'runtime' => $data['runtime'] ?? null,
            'vote_average' => $data['vote_average'] ?? null,
            'content_rating' => $this->movieRating($data['release_dates']['results'] ?? []),
            'language' => $data['original_language'] ?? null,
            'trailer_key' => $trailerKey,
            'trailer_url' => $trailerKey ? 'https://www.youtube.com/watch?v=' . $trailerKey : null,
            'seo_title' => $data['title'] . ' (' . substr($data['release_date'] ?? '', 0, 4) . ') — Movie Details',
            'meta_description' => Str::limit($data['overview'] ?? '', 155),
            'keywords' => collect($data['genres'] ?? [])->pluck('name')->implode(', '),
            'is_active' => true,
        ]);

        $movie->genres()->sync($this->genreIds($data['genres'] ?? []));

        return [
            'title' => $movie->title,
            'status' => 'imported',
            'tmdb_id' => $movie->tmdb_id,
        ];
    }

    private function importShow(int $id, TmdbService $tmdb, MediaStorageService $media): array
    {
        $data = $tmdb->show($id);
        $old = null;

        $poster = $old?->poster_path ?: $media->storeRemoteImage($data['poster_path'] ?? null, 'shows/posters', 'w780');
        $backdrop = $old?->backdrop_path ?: $media->storeRemoteImage($data['backdrop_path'] ?? null, 'shows/backdrops', 'original');
        $trailerKey = $this->trailer($data['videos']['results'] ?? []);
        $topCast = !empty($old?->top_cast) ? $old->top_cast : $this->storeTopCast($data['credits']['cast'] ?? [], $media, 'shows/cast');

        $show = Show::create([
            'tmdb_id' => $data['id'],
            'imdb_id' => $data['external_ids']['imdb_id'] ?? null,
            'title' => $data['name'],
            'slug' => $old?->slug ?: $this->uniqueShowSlug($data['name'], substr($data['first_air_date'] ?? '', 0, 4)),
            'overview' => $data['overview'] ?? '',
            'poster_path' => $poster,
            'backdrop_path' => $backdrop,
            'top_cast' => $topCast,
            'first_air_date' => !empty($data['first_air_date']) ? $data['first_air_date'] : null,
            'vote_average' => $data['vote_average'] ?? null,
            'content_rating' => $this->showRating($data['content_ratings']['results'] ?? []),
            'trailer_key' => $trailerKey,
            'seo_title' => $data['name'] . ' TV show details',
            'meta_description' => Str::limit($data['overview'] ?? '', 155),
            'keywords' => collect($data['genres'] ?? [])->pluck('name')->implode(', '),
            'is_active' => true,
        ]);

        $show->genres()->sync($this->genreIds($data['genres'] ?? []));

        $seasonNumbers = collect($data['seasons'] ?? [])
            ->pluck('season_number')
            ->filter(fn ($number) => $number !== null)
            ->map(fn ($number) => (int) $number)
            ->unique()
            ->sort()
            ->values()
            ->all();

        return [
            'title' => $show->title,
            'status' => 'imported',
            'tmdb_id' => $show->tmdb_id,
            'show_id' => $show->id,
            'season_numbers' => $seasonNumbers,
        ];
    }

    private function genreIds(array $genres): array
    {
        return collect($genres)->map(function (array $genre) {
            return Genre::updateOrCreate(
                ['tmdb_id' => $genre['id']],
                ['name' => $genre['name'], 'slug' => Str::slug($genre['name']), 'is_active' => true]
            )->id;
        })->all();
    }

    private function storeTopCast(array $cast, MediaStorageService $media, string $folder): array
    {
        return collect($cast)->filter(fn ($person) => !empty($person['name']))->take(12)
            ->map(function ($person) use ($media, $folder) {
                $image = null;
                if (!empty($person['profile_path'])) {
                    try {
                        $image = $media->storeRemoteImage($person['profile_path'], $folder, 'w342');
                    } catch (Throwable $exception) {
                        report($exception);
                    }
                }
                return [
                    'tmdb_id' => $person['id'] ?? null,
                    'name' => $person['name'],
                    'character' => $person['character'] ?? '',
                    'image' => $image,
                ];
            })->values()->all();
    }

    private function trailer(array $videos): ?string
    {
        $video = collect($videos)->first(fn ($item) =>
            ($item['site'] ?? '') === 'YouTube' && in_array($item['type'] ?? '', ['Trailer', 'Teaser'], true)
        );
        return $video['key'] ?? null;
    }

    private function movieRating(array $ratings): ?string
    {
        $us = collect($ratings)->firstWhere('iso_3166_1', 'US');
        $certification = collect($us['release_dates'] ?? [])->first(fn ($row) => !empty($row['certification']));
        return $certification['certification'] ?? null;
    }

    private function showRating(array $ratings): ?string
    {
        return collect($ratings)->firstWhere('iso_3166_1', 'US')['rating'] ?? null;
    }

    private function uniqueMovieSlug(string $title, string $year): string
    {
        $base = Str::slug(trim($title . ' ' . $year)) ?: 'movie';
        $slug = $base;
        $i = 2;
        while (Movie::where('slug', $slug)->exists()) {
            $slug = $base . '-' . $i++;
        }
        return $slug;
    }

    private function uniqueShowSlug(string $title, string $year): string
    {
        $base = Str::slug(trim($title . ' ' . $year)) ?: 'show';
        $slug = $base;
        $i = 2;
        while (Show::where('slug', $slug)->exists()) {
            $slug = $base . '-' . $i++;
        }
        return $slug;
    }
}
