<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\Anime;
use App\Models\Episode;
use App\Models\ImportJob;
use App\Models\Season;
use App\Models\Setting;
use App\Models\Subtitle;
use App\Models\VideoSource;
use App\Services\DeepSeekService;
use App\Services\JikanService;
use Illuminate\Http\Request;
use Illuminate\Support\Str;

class ImportApiController extends Controller
{
    // ── Anime eşleştirme yardımcısı ──────────────────────────────────────────
    // Her iki kaynak da bu helper'ı kullanır: mal_id → slug → normalize → oluştur

    private function findOrCreateAnime(array $data): Anime
    {
        // 1. mal_id — en güvenilir
        if (!empty($data['mal_id'])) {
            $anime = Anime::where('mal_id', $data['mal_id'])->first();
            if ($anime) return $anime;
        }

        // 2. slug
        $slug = Str::slug($data['title'] ?? '');
        if ($slug) {
            $anime = Anime::where('slug', $slug)->first();
            if ($anime) {
                // mal_id eksikse güncelle
                if (!empty($data['mal_id']) && !$anime->mal_id) {
                    $anime->update(['mal_id' => $data['mal_id']]);
                }
                return $anime;
            }
        }

        // 3. Büyük/küçük harf duyarsız başlık + title_en eşleşmesi
        $lowerTitle = strtolower(trim($data['title'] ?? ''));
        if ($lowerTitle) {
            $anime = Anime::whereRaw('LOWER(title) = ?', [$lowerTitle])
                ->orWhereRaw('LOWER(title_en) = ?', [$lowerTitle])
                ->first();
            if ($anime) {
                if (!empty($data['mal_id']) && !$anime->mal_id) {
                    $anime->update(['mal_id' => $data['mal_id']]);
                }
                return $anime;
            }
        }

        // 4. Bulunamadı → yeni oluştur
        return Anime::create([
            'title'        => $data['title'],
            'title_en'     => $data['title_en']  ?? '',
            'slug'         => $slug ?: Str::slug($data['title'] ?? 'anime-' . uniqid()),
            'type'         => $data['type']       ?? 'series',
            'status'       => 'ongoing',
            'is_published' => false,
            'cover_image'  => $data['cover']      ?? null,
            'mal_id'       => $data['mal_id']     ?? null,
        ]);
    }

    // ── Auto-Import API'leri ─────────────────────────────────────────────────

    /**
     * Programatik job oluşturma.
     * AnimeCix ve Anizium her iki kaynak için tek endpoint.
     */
    public function createJob(Request $request)
    {
        $source = $request->input('source', 'anizium');

        // ── AnimeCix job ──────────────────────────────────────────────────────
        if ($source === 'animecix') {
            if ($request->has('year') && $request->year !== null) {
                $request->merge(['year' => (string) $request->year]);
            }

            $data = $request->validate([
                'animecix_title_id' => 'required|string|max:50',
                'slug'              => 'required|string|max:300',
                'title'             => 'required|string|max:300',
                'title_en'          => 'nullable|string|max:300',
                'cover'             => 'nullable|string|max:500',
                'episode_count'     => 'nullable|integer',
                'type'              => 'nullable|string|max:30',
                'year'              => 'nullable|string|max:10',
                'genres'            => 'nullable|array',
                'mal_id'            => 'nullable|integer',
                'priority'          => 'nullable|integer|min:0|max:2',
            ]);

            // Dedup — aynı title zaten aktif/bitti mi?
            $existing = ImportJob::where('source', 'animecix')
                ->where('animecix_title_id', $data['animecix_title_id'])
                ->whereIn('status', ['pending', 'fetching', 'done'])
                ->latest()->first();

            if ($existing) {
                return response()->json([
                    'job_id' => $existing->id,
                    'status' => 'existing',
                ]);
            }

            // Anime bul veya oluştur (unified matcher)
            $anime = $this->findOrCreateAnime([
                'mal_id'   => $data['mal_id'] ?? null,
                'title'    => $data['title'],
                'title_en' => $data['title_en'] ?? '',
                'slug'     => Str::slug($data['title']),
                'type'     => $data['type'] ?: 'series',
                'cover'    => $data['cover'] ?? null,
            ]);

            // Priority: request'ten geliyorsa kullan, yoksa otomatik hesapla
            $priority = (int) ($data['priority'] ?? ImportJob::PRIORITY_NEW);
            if (!isset($data['priority']) && !$anime->wasRecentlyCreated) {
                $hasAnizium = VideoSource::whereHas('episode', fn($q) => $q->where('anime_id', $anime->id))
                    ->where('source', 'anizium')->exists();
                $priority = $hasAnizium ? ImportJob::PRIORITY_CROSSFILL : ImportJob::PRIORITY_NEW;
            }

            $job = ImportJob::create([
                'source'            => 'animecix',
                'animecix_title_id' => $data['animecix_title_id'],
                'animecix_slug'     => $data['slug'],
                'anime_title'       => $data['title'],
                'anime_id'          => $anime->id,
                'status'            => 'pending',
                'priority'          => $priority,
            ]);

            // AniList resimlerini arka planda doldur
            if (empty($anime->cover_image) || empty($anime->banner_image)) {
                dispatch(function () use ($anime) {
                    try { (new \App\Services\AniListService())->fillImages($anime->fresh()); }
                    catch (\Throwable) {}
                })->afterResponse();
            }

            return response()->json(['job_id' => $job->id, 'status' => 'created', 'anime_id' => $anime->id], 201);
        }

        // ── Anizium job ───────────────────────────────────────────────────────
        $data = $request->validate([
            'source_url'              => 'required|string|max:500',
            'anime_title'             => 'required|string|max:300',
            'watch_id'                => 'required|string|max:50',
            'season_ranges'           => 'nullable|array',
            'season_ranges.*.season'  => 'required_with:season_ranges|integer|min:1',
            'season_ranges.*.from'    => 'required_with:season_ranges|integer|min:1',
            'season_ranges.*.to'      => 'required_with:season_ranges|integer|min:1',
            'priority'                => 'nullable|integer|min:0|max:2',
        ]);

        // Aktif job var mı?
        $active = ImportJob::where('watch_id', $data['watch_id'])
            ->whereIn('status', ['pending', 'fetching', 'downloading', 'uploading'])
            ->latest()->first();

        if ($active) {
            return response()->json(['job_id' => $active->id, 'status' => 'existing', 'msg' => 'Aktif job zaten var.']);
        }

        // Daha önce bitti mi? (anime_id'yi al)
        $prevDone = ImportJob::where('watch_id', $data['watch_id'])
            ->where('status', 'done')->whereNotNull('anime_id')->latest()->first();

        // Anime eşleştir (watch_id'den tanınan anime_id varsa kullan, yoksa title arama)
        $anime = null;
        if ($prevDone?->anime_id) {
            $anime = Anime::find($prevDone->anime_id);
        }
        if (!$anime) {
            // Başlık ile mevcut anime bul (farklı kaynaktan yüklenmiş olabilir)
            $slug  = Str::slug($data['anime_title']);
            $lower = strtolower(trim($data['anime_title']));
            $anime = Anime::where('slug', $slug)
                ->orWhereRaw('LOWER(title) = ?', [$lower])
                ->orWhereRaw('LOWER(title_en) = ?', [$lower])
                ->first();
        }

        // Priority: request'ten geliyorsa kullan, yoksa otomatik hesapla
        $aniziumPriority = (int) ($data['priority'] ?? ImportJob::PRIORITY_NEW);
        if (!isset($data['priority']) && $anime?->id) {
            $hasAnimecix = VideoSource::whereHas('episode', fn($q) => $q->where('anime_id', $anime->id))
                ->where('source', 'animecix')->exists();
            $aniziumPriority = $hasAnimecix ? ImportJob::PRIORITY_CROSSFILL : ImportJob::PRIORITY_NEW;
        }

        $job = ImportJob::create([
            'source'        => 'anizium',
            'source_url'    => $data['source_url'],
            'anime_title'   => $data['anime_title'],
            'watch_id'      => $data['watch_id'],
            'status'        => 'pending',
            'anime_id'      => $anime?->id,
            'season_ranges' => $data['season_ranges'] ?? null,
            'priority'      => $aniziumPriority,
        ]);

        return response()->json(['job_id' => $job->id, 'status' => 'created', 'priority' => $aniziumPriority], 201);
    }

    // ── Anime arama endpoint'i — Python botları için ──────────────────────────
    // GET /api/import/anime/lookup?mal_id=xxx OR ?title=yyy OR ?slug=zzz

    public function animeLookup(Request $request)
    {
        // 1. mal_id
        if ($mal_id = $request->input('mal_id')) {
            $anime = Anime::where('mal_id', (int) $mal_id)->first();
            if ($anime) {
                return response()->json([
                    'found'    => true,
                    'anime_id' => $anime->id,
                    'title'    => $anime->title,
                    'mal_id'   => $anime->mal_id,
                ]);
            }
        }

        // 2. Slug veya başlık
        if ($title = $request->input('title')) {
            $slug  = Str::slug($title);
            $lower = strtolower(trim($title));
            $anime = Anime::where('slug', $slug)
                ->orWhereRaw('LOWER(title) = ?', [$lower])
                ->orWhereRaw('LOWER(title_en) = ?', [$lower])
                ->first();
            if ($anime) {
                return response()->json([
                    'found'    => true,
                    'anime_id' => $anime->id,
                    'title'    => $anime->title,
                    'mal_id'   => $anime->mal_id,
                ]);
            }
        }

        return response()->json(['found' => false, 'anime_id' => null]);
    }

    // ── Animecix: bekleyen job listesi ────────────────────────────────────────
    public function animecixPendingJobs()
    {
        try {
            $jobs = ImportJob::where('source', 'animecix')
                ->where('status', 'pending')
                ->orderByDesc('priority')
                ->orderBy('id')
                ->limit(20)
                ->get(['id', 'animecix_title_id', 'animecix_slug', 'anime_title', 'anime_id', 'priority']);
        } catch (\Throwable) {
            $jobs = ImportJob::where('source', 'animecix')
                ->where('status', 'pending')
                ->orderBy('id')
                ->limit(20)
                ->get(['id', 'animecix_title_id', 'animecix_slug', 'anime_title', 'anime_id']);
        }

        return response()->json(['jobs' => $jobs, 'count' => $jobs->count()]);
    }

    // ── Animecix: episode'lara video kaynakları kaydet ───────────────────────
    public function saveVideoSources(Request $request, Episode $episode)
    {
        $data = $request->validate([
            'sources'                    => 'required|array|min:1',
            'sources.*.label'            => 'nullable|string|max:120',
            'sources.*.url'              => 'required|string|max:2000',
            'sources.*.type'             => 'nullable|in:mp4,hls,embed',
            'sources.*.quality'          => 'nullable|string|max:20',
            'sources.*.translator_id'    => 'nullable|string|max:60',
            'sources.*.sort'             => 'nullable|integer',
        ]);

        // Önceki AnimeCix kaynaklarını sil (idempotent yeniden çalıştırma)
        VideoSource::where('episode_id', $episode->id)->where('source', 'animecix')->delete();

        // AnimeCix kaynakları eklendiğinde Anizium '4K' kaynağını secondary yap
        VideoSource::where('episode_id', $episode->id)
            ->where('source', 'anizium')
            ->update(['is_default' => false, 'sort_order' => 99]);

        $isDefault = true;
        foreach ($data['sources'] as $idx => $src) {
            VideoSource::create([
                'episode_id'    => $episode->id,
                'label'         => $src['label'] ?? '',
                'url'           => $src['url'],
                'type'          => $src['type'] ?? 'mp4',
                'quality'       => $src['quality'] ?? '',
                'translator_id' => $src['translator_id'] ?? null,
                'sort_order'    => $src['sort'] ?? $idx,
                'is_default'    => $isDefault,
                'source'        => 'animecix',
            ]);
            $isDefault = false;

            // İlk AnimeCix kaynağını episode video_url olarak da kaydet
            if ($idx === 0) {
                $url  = $src['url'];
                $type = $src['type'] ?? 'mp4';
                if ($type === 'mp4') {
                    $episode->update(['video_url' => $url, 'source' => 'animecix']);
                } elseif ($type === 'hls') {
                    $episode->update(['m3u8_url' => $url, 'source' => 'animecix']);
                }
            }
        }

        // Anime yayınla (ilk bölüm geldiğinde)
        if ($episode->anime_id) {
            Anime::where('id', $episode->anime_id)->where('is_published', false)->update(['is_published' => true]);
        }

        return response()->json(['ok' => true, 'saved' => count($data['sources'])]);
    }

    /**
     * Import edilmiş tüm watch_id'leri döndürür (discover.py karşılaştırması için).
     * NOT: Artık sadece failed-olmayanlar "mevcut" sayılır.
     */
    public function importedIds()
    {
        $ids = ImportJob::whereNotNull('watch_id')
            ->where('watch_id', '!=', '')
            ->whereIn('status', ['pending', 'fetching', 'downloading', 'uploading', 'done'])
            ->pluck('watch_id')
            ->map(fn($id) => (string) $id)
            ->unique()
            ->values();

        return response()->json(['watch_ids' => $ids, 'count' => $ids->count()]);
    }

    /**
     * Animexe'deki tüm yayınlanan anime başlıklarını döndürür.
     */
    public function importedTitles()
    {
        $titles = Anime::where('is_published', true)
            ->pluck('title')
            ->filter()
            ->unique()
            ->values();

        return response()->json(['titles' => $titles, 'count' => $titles->count()]);
    }

    /**
     * Bot 3 güncelleme botu için: Anizium watch_id'si olan TÜM animeleri döndür.
     * ongoing/completed/finished fark etmez — her anime eksik bölüm kontrolüne tabi.
     * Her animenin mevcut sezon/bölüm durumu da dahil.
     */
    public function allAniziumAnimes()
    {
        $animes = ImportJob::where('import_jobs.status', 'done')
            ->whereNotNull('import_jobs.watch_id')
            ->whereNotNull('import_jobs.anime_id')
            ->join('animes', 'animes.id', '=', 'import_jobs.anime_id')
            ->select(
                'import_jobs.watch_id',
                'import_jobs.anime_title',
                'import_jobs.anime_id',
                'animes.status as anime_status'
            )
            ->groupBy('import_jobs.watch_id', 'import_jobs.anime_title', 'import_jobs.anime_id', 'animes.status')
            ->get();

        $result = $animes->map(function ($a) {
            $seasonData = \DB::table('episodes')
                ->join('seasons', 'seasons.id', '=', 'episodes.season_id')
                ->where('episodes.anime_id', $a->anime_id)
                ->where('episodes.is_published', true)
                ->select(
                    'seasons.season_number',
                    \DB::raw('MAX(episodes.episode_number) as max_episode'),
                    \DB::raw('COUNT(*) as episode_count')
                )
                ->groupBy('seasons.season_number')
                ->orderBy('seasons.season_number')
                ->get();

            $seasons = [];
            foreach ($seasonData as $s) {
                $seasons[(string) $s->season_number] = [
                    'count' => (int) $s->episode_count,
                    'max'   => (int) $s->max_episode,
                ];
            }

            return [
                'watch_id'     => $a->watch_id,
                'anime_title'  => $a->anime_title,
                'anime_id'     => (int) $a->anime_id,
                'anime_status' => $a->anime_status,
                'seasons'      => $seasons,
            ];
        });

        return response()->json(['animes' => $result, 'count' => $result->count()]);
    }

    // ── Bot ayarlarını döndür ─────────────────────────────────────────────────
    public function settings()
    {
        $get = fn($key) => \App\Models\Setting::where('key', $key)->value('value') ?? '';

        return response()->json([
            'bunnycdn' => [
                'zone'     => $get('bunnycdn_zone'),
                'api_key'  => $get('bunnycdn_api_key'),
                'pull_url' => $get('bunnycdn_pull_url'),
            ],
        ]);
    }

    // Bağlantı testi
    public function test()
    {
        $pendingBySource = ImportJob::where('status', 'pending')
            ->selectRaw('COALESCE(source, "anizium") as source, COUNT(*) as cnt')
            ->groupBy('source')
            ->pluck('cnt', 'source');

        return response()->json([
            'ok'               => true,
            'message'          => 'Laravel API erişilebilir',
            'db'               => \DB::connection()->getDatabaseName(),
            'pending'          => ImportJob::where('status', 'pending')->count(),
            'pending_anizium'  => (int) ($pendingBySource['anizium'] ?? 0),
            'pending_animecix' => (int) ($pendingBySource['animecix'] ?? 0),
            'total'            => ImportJob::count(),
            'timestamp'        => now()->toDateTimeString(),
        ]);
    }

    // Dashboard istatistikleri
    public function stats()
    {
        $counts = ImportJob::selectRaw('status, COUNT(*) as cnt')
            ->groupBy('status')
            ->pluck('cnt', 'status');

        $byStatus = [
            'pending'     => (int) ($counts['pending']     ?? 0),
            'fetching'    => (int) ($counts['fetching']    ?? 0),
            'downloading' => (int) ($counts['downloading'] ?? 0),
            'uploading'   => (int) ($counts['uploading']   ?? 0),
            'done'        => (int) ($counts['done']        ?? 0),
            'failed'      => (int) ($counts['failed']      ?? 0),
        ];

        $active = ImportJob::whereIn('status', ['fetching', 'downloading', 'uploading'])
            ->latest()->first();

        $ongoingCount = ImportJob::where('import_jobs.status', 'done')
            ->whereNotNull('import_jobs.anime_id')
            ->join('animes', 'animes.id', '=', 'import_jobs.anime_id')
            ->where('animes.status', 'ongoing')
            ->distinct('import_jobs.watch_id')
            ->count('import_jobs.watch_id');

        return response()->json([
            'total'         => array_sum($byStatus),
            'by_status'     => $byStatus,
            'ongoing_count' => $ongoingCount,
            'active_job'    => $active ? [
                'id'             => $active->id,
                'title'          => $active->anime_title,
                'status'         => $active->status,
                'current_step'   => $active->current_step,
                'total_episodes' => (int) ($active->total_episodes ?? 0),
                'done_episodes'  => (int) ($active->done_episodes  ?? 0),
                'progress_pct'   => $active->progress_percent,
            ] : null,
        ]);
    }

    // Python: belirli bir job'u al
    public function getJob(ImportJob $job)
    {
        return response()->json(['job' => $job]);
    }

    /**
     * Python: bekleyen job var mı? — DB lock ile atomik al.
     * Her kaynak kendi job'larını alır; çapraz engelleme KALDIRILDI.
     * AnimeCix kendi kuyruğunu /animecix/pending ile alıyor.
     * Bu endpoint sadece Anizium (ve untagged legacy) job'larını döndürür.
     */
    public function nextJob()
    {
        $job = \DB::transaction(function () {
            // Priority sırası: 2 (cross-fill) → 1 (ongoing) → 0 (yeni keşif)
            // priority kolonu henüz yoksa (migration çalıştırılmadıysa) sadece id sıralaması
            try {
                $job = ImportJob::where('status', 'pending')
                    ->where(fn($q) =>
                        $q->where('source', 'anizium')
                          ->orWhere('source', '')
                          ->orWhereNull('source')
                    )
                    ->orderByDesc('priority')
                    ->orderByRaw('CASE WHEN season_ranges IS NOT NULL THEN 1 ELSE 0 END DESC')
                    ->orderBy('id')
                    ->lockForUpdate()
                    ->first();
            } catch (\Throwable) {
                $job = ImportJob::where('status', 'pending')
                    ->where(fn($q) =>
                        $q->where('source', 'anizium')
                          ->orWhere('source', '')
                          ->orWhereNull('source')
                    )
                    ->orderByRaw('CASE WHEN season_ranges IS NOT NULL THEN 1 ELSE 0 END DESC')
                    ->orderBy('id')
                    ->lockForUpdate()
                    ->first();
            }

            if ($job) {
                $job->update(['status' => 'fetching']);
            }
            return $job;
        });

        return response()->json(['job' => $job]);
    }

    // Daemon: import edilmiş ongoing animeleri döndür (yeni bölüm kontrolü için)
    public function ongoingAnimes()
    {
        $animes = ImportJob::where('import_jobs.status', 'done')
            ->whereNotNull('import_jobs.watch_id')
            ->whereNotNull('import_jobs.anime_id')
            ->join('animes', 'animes.id', '=', 'import_jobs.anime_id')
            ->where('animes.status', 'ongoing')
            ->select(
                'import_jobs.watch_id',
                'import_jobs.anime_title',
                'import_jobs.anime_id'
            )
            ->groupBy('import_jobs.watch_id', 'import_jobs.anime_title', 'import_jobs.anime_id')
            ->get();

        $result = $animes->map(function ($a) {
            $seasonData = \DB::table('episodes')
                ->join('seasons', 'seasons.id', '=', 'episodes.season_id')
                ->where('episodes.anime_id', $a->anime_id)
                ->where('episodes.is_published', true)
                ->select(
                    'seasons.season_number',
                    \DB::raw('MAX(episodes.episode_number) as max_episode'),
                    \DB::raw('COUNT(*) as episode_count')
                )
                ->groupBy('seasons.season_number')
                ->orderBy('seasons.season_number')
                ->get();

            $seasons = [];
            foreach ($seasonData as $s) {
                $seasons[(string) $s->season_number] = [
                    'count' => (int) $s->episode_count,
                    'max'   => (int) $s->max_episode,
                ];
            }

            return [
                'watch_id'    => $a->watch_id,
                'anime_title' => $a->anime_title,
                'anime_id'    => (int) $a->anime_id,
                'seasons'     => $seasons,
            ];
        });

        return response()->json(['animes' => $result, 'count' => $result->count()]);
    }

    // Python: job durumunu güncelle
    public function updateStatus(Request $request, ImportJob $job)
    {
        $data = $request->validate([
            'status'          => 'sometimes|in:pending,fetching,downloading,uploading,done,failed',
            'current_step'    => 'nullable|string',
            'total_episodes'  => 'nullable|integer',
            'done_episodes'   => 'nullable|integer',
            'failed_episodes' => 'nullable|integer',
            'error_log'       => 'nullable|string',
        ]);

        $update = array_intersect_key($data, array_flip(array_keys($request->all())));
        if (!empty($update)) {
            $job->update($update);
        }

        return response()->json(['ok' => true]);
    }

    // Python: bir bölüm tamamlandı, DB'ye kaydet
    public function saveEpisode(Request $request, ImportJob $job)
    {
        $data = $request->validate([
            'season'          => 'required|integer|min:1',
            'episode'         => 'required|integer|min:0',
            'title'           => 'nullable|string',
            'description'     => 'nullable|string',
            'duration'        => 'nullable|integer',
            'video_url'       => 'nullable|string',
            'm3u8_url'        => 'nullable|string',
            'bunny_video_id'  => 'nullable|string',
            'source_url'      => 'nullable|string',
            'thumbnail'       => 'nullable|string',
            'available_dubs'  => 'nullable|array',
            'available_dubs.*'=> 'string|max:32',
            'embed_source'            => 'nullable|string|max:32',
            'extra_sources'           => 'nullable|array',
            'extra_sources.*.url'     => 'required|string|max:2000',
            'extra_sources.*.quality' => 'nullable|string|max:20',
            'extra_sources.*.label'   => 'nullable|string|max:60',
        ]);

        $isAnizium = in_array($job->source ?? 'anizium', ['anizium', '', null], true)
                  || is_null($job->source);

        // Anime bul veya oluştur
        if ($job->anime_id) {
            $anime = Anime::find($job->anime_id);
        } else {
            $baseTitle = $job->anime_title ?: "Anime CDN-{$job->cdn_id}";
            $slug      = Str::slug($baseTitle) . '-' . ($job->cdn_id ?: $job->id);

            $anime = Anime::firstOrCreate(
                ['slug' => $slug],
                [
                    'title'        => $baseTitle,
                    'type'         => 'series',
                    'status'       => 'ongoing',
                    'is_published' => true,
                ]
            );
            $job->update(['anime_id' => $anime->id]);

            // Auto-fetch MAL ID (fire-and-forget)
            if (!$anime->mal_id) {
                dispatch(function () use ($anime) {
                    try {
                        $jikan = new JikanService();
                        $malId = $jikan->searchMalId($anime->title, $anime->title_en, $anime->title_jp);
                        if ($malId) {
                            $anime->update(['mal_id' => $malId]);
                            $chain = $jikan->fetchSeasonMalIds($malId);
                            foreach ($anime->seasons()->orderBy('season_number')->get() as $i => $season) {
                                if (!$season->mal_id && isset($chain[$i])) {
                                    $season->update(['mal_id' => $chain[$i]]);
                                }
                            }
                            (new \App\Services\AniListService())->fillImages($anime->fresh());
                        }
                    } catch (\Throwable) {}
                })->afterResponse();
            }

            if (empty($anime->cover_image) || empty($anime->banner_image)) {
                dispatch(function () use ($anime) {
                    try { (new \App\Services\AniListService())->fillImages($anime->fresh()); }
                    catch (\Throwable) {}
                })->afterResponse();
            }

            if (Setting::get('ai_auto_seo') === '1' && empty($anime->seo_title)) {
                dispatch(function () use ($anime) {
                    try {
                        $ai     = new \App\Services\DeepSeekService();
                        $result = $ai->generateAnimeSeoMeta($anime->fresh(['genres']));
                        if ($result) {
                            $anime->update([
                                'seo_title'     => $result['seo_title']     ?? null,
                                'seo_meta_desc' => $result['seo_meta_desc'] ?? null,
                                'seo_keywords'  => $result['seo_keywords']  ?? null,
                            ]);
                        }
                    } catch (\Throwable) {}
                })->afterResponse();
            }
        }

        // Sezon bul/oluştur
        $season = Season::firstOrCreate(
            ['anime_id' => $anime->id, 'season_number' => $data['season']],
            ['is_published' => true]
        );

        if (!$season->mal_id && $anime->mal_id) {
            dispatch(function () use ($anime, $season) {
                try {
                    $chain = (new JikanService())->fetchSeasonMalIds($anime->mal_id);
                    $idx   = $season->season_number - 1;
                    if (isset($chain[$idx])) $season->update(['mal_id' => $chain[$idx]]);
                } catch (\Throwable) {}
            })->afterResponse();
        }

        // Mevcut episode var mı? (başka kaynaktan yüklenmiş olabilir)
        $existingEpisode = Episode::where('season_id', $season->id)
            ->where('episode_number', $data['episode'])
            ->first();

        // Başlık stratejisi:
        // Anizium → her zaman başlığı set eder (kullanıcı isteği: başlık aniziumdan gelsin)
        // AnimeCix → sadece mevcut başlık boşsa set eder
        $titleValue = $data['title'] ?? null;
        if (!$isAnizium && $existingEpisode && $existingEpisode->title) {
            $titleValue = $existingEpisode->title; // AnimeCix mevcut başlığı ezip geçmez
        }

        $episodeValues = [
            'anime_id'       => $anime->id,
            'title'          => $titleValue,
            'description'    => $data['description'] ?? null,
            'duration'       => $data['duration'] ?? null,
            'source_url'     => $data['source_url'] ?? null,
            'thumbnail'      => $data['thumbnail'] ?? null,
            'status'         => 'published',
            'is_published'   => true,
        ];

        // video_url / m3u8_url sadece Anizium koyar (AnimeCix video_sources'tan gider)
        if ($isAnizium) {
            $episodeValues['video_url']       = $data['video_url'] ?? null;
            $episodeValues['m3u8_url']        = $data['m3u8_url'] ?? null;
            $episodeValues['bunny_video_id']  = $data['bunny_video_id'] ?? null;
            $episodeValues['available_dubs']  = isset($data['available_dubs']) ? json_encode($data['available_dubs']) : null;
            $episodeValues['source']          = isset($data['bunny_video_id']) ? 'bunnycdn' : ($data['embed_source'] ?? 'anizium');
        }

        try {
            Episode::updateOrCreate(
                ['season_id' => $season->id, 'episode_number' => $data['episode']],
                $episodeValues
            );
        } catch (\Illuminate\Database\QueryException $e) {
            if (str_contains($e->getMessage(), 'available_dubs')) {
                unset($episodeValues['available_dubs']);
                Episode::updateOrCreate(
                    ['season_id' => $season->id, 'episode_number' => $data['episode']],
                    $episodeValues
                );
            } else {
                throw $e;
            }
        }

        $savedEp = Episode::where('season_id', $season->id)
            ->where('episode_number', $data['episode'])
            ->first();

        // ── Anizium bölümlerini video_sources tablosuna kaydet ──
        if ($isAnizium && $savedEp) {
            $vsUrl  = $data['video_url'] ?? $data['m3u8_url'] ?? null;
            $vsType = (!empty($data['video_url'])) ? 'mp4'
                    : (!empty($data['m3u8_url']) ? 'hls' : null);

            if ($vsUrl && $vsType) {
                $hasAnimecix = VideoSource::where('episode_id', $savedEp->id)
                    ->where('source', 'animecix')
                    ->exists();

                // Mevcut anizium kaynaklarını temizle, yeniden ekle
                VideoSource::where('episode_id', $savedEp->id)
                    ->where('source', 'anizium')
                    ->delete();

                // Ana kaynak (en yüksek kalite)
                VideoSource::create([
                    'episode_id' => $savedEp->id,
                    'source'     => 'anizium',
                    'label'      => '4K',
                    'url'        => $vsUrl,
                    'type'       => $vsType,
                    'quality'    => '4K',
                    'sort_order' => $hasAnimecix ? 99 : 0,
                    'is_default' => !$hasAnimecix,
                ]);

                // Yedek kaliteler (720p, 480p vs. — HEVC failse browser bunları dener)
                foreach (($data['extra_sources'] ?? []) as $idx => $src) {
                    VideoSource::create([
                        'episode_id' => $savedEp->id,
                        'source'     => 'anizium',
                        'label'      => $src['label'] ?? ($src['quality'] ?? 'Yedek'),
                        'url'        => $src['url'],
                        'type'       => 'hls',
                        'quality'    => $src['quality'] ?? null,
                        'sort_order' => ($hasAnimecix ? 99 : 0) + $idx + 1,
                        'is_default' => false,
                    ]);
                }
            }
        }

        $anime->update(['episode_count' => $anime->episodes()->count()]);
        $job->increment('done_episodes');

        // Anime yayınla
        Anime::where('id', $anime->id)->where('is_published', false)->update(['is_published' => true]);

        // Auto açıklama üretimi (Anizium için)
        if ($isAnizium && Setting::get('ai_auto_description') === '1' && $savedEp && empty($savedEp->description)) {
            $ai   = new DeepSeekService();
            $desc = $ai->generateEpisodeDescription($anime->title, $data['episode'], $data['title'] ?? '');
            if ($desc) $savedEp->update(['description' => $desc]);
        }

        return response()->json(['ok' => true, 'anime_id' => $anime->id, 'episode_id' => $savedEp?->id]);
    }

    /**
     * Python: job için tamamlanmış bölümleri döndür (resume desteği).
     *
     * Her kaynak sadece KENDİ kaydettiği bölümleri "done" sayar.
     * Anizium → video_sources.source='anizium' olan bölümler
     * AnimeCix → video_sources.source='animecix' olan bölümler
     * Legacy (source belirsiz) → eski davranış (is_published=true)
     */
    public function doneEpisodes(ImportJob $job)
    {
        if (!$job->anime_id) {
            return response()->json(['done' => (object)[]]);
        }

        $source = $job->source ?? 'anizium';

        if (in_array($source, ['anizium', 'animecix'], true)) {
            // Kaynak bazlı: sadece bu kaynağın video_sources kayıtları olan bölümler
            $rows = \DB::table('video_sources')
                ->join('episodes', 'episodes.id', '=', 'video_sources.episode_id')
                ->join('seasons',  'seasons.id',  '=', 'episodes.season_id')
                ->where('episodes.anime_id', $job->anime_id)
                ->where('video_sources.source', $source)
                ->select('seasons.season_number as s', 'episodes.episode_number as e')
                ->distinct()
                ->get();
        } else {
            // Legacy: is_published=true olan tüm bölümler
            $rows = \DB::table('episodes')
                ->join('seasons', 'seasons.id', '=', 'episodes.season_id')
                ->where('episodes.anime_id', $job->anime_id)
                ->where('episodes.is_published', true)
                ->select('seasons.season_number as s', 'episodes.episode_number as e')
                ->get();
        }

        $done = [];
        foreach ($rows as $r) {
            $done[(string)$r->s][(string)$r->e] = true;
        }

        return response()->json(['done' => $done ?: (object)[]]);
    }

    // Python: bir bölüme altyazı kaydet
    public function saveSubtitle(Request $request, ImportJob $job)
    {
        $data = $request->validate([
            'season'     => 'required|integer|min:1',
            'episode'    => 'required|integer|min:1',
            'language'   => 'required|string|max:10',
            'label'      => 'required|string|max:50',
            'url'        => 'required|string',
            'is_default' => 'boolean',
        ]);

        $season  = Season::where('anime_id', $job->anime_id)
                         ->where('season_number', $data['season'])->first();
        if (!$season) {
            return response()->json(['ok' => false, 'msg' => 'Season bulunamadı'], 404);
        }

        $episode = Episode::where('season_id', $season->id)
                          ->where('episode_number', $data['episode'])->first();
        if (!$episode) {
            return response()->json(['ok' => false, 'msg' => 'Episode bulunamadı'], 404);
        }

        Subtitle::updateOrCreate(
            ['episode_id' => $episode->id, 'language' => $data['language']],
            ['label' => $data['label'], 'url' => $data['url'], 'is_default' => $data['is_default'] ?? false]
        );

        return response()->json(['ok' => true]);
    }

    // Anizium kaynaklı tüm bölümleri watch_id bazında döndür (altyazı yenileme için)
    // ?only_missing_subs=1 → subtitles tablosunda kaydı olmayan bölümler
    // ?fix_anizium_subs=1  → altyazısı var ama URL'i hâlâ ham Anizium linki olan bölümler (b-cdn.net değil)
    public function aniziumEpisodes(Request $request)
    {
        $onlyMissing     = $request->boolean('only_missing_subs', false);
        $fixAniziumSubs  = $request->boolean('fix_anizium_subs',  false);

        $query = \DB::table('episodes')
            ->join('seasons', 'seasons.id', '=', 'episodes.season_id')
            ->whereNotNull('episodes.source_url')
            ->where('episodes.source_url', 'like', '%anizium.co/watch/%')
            ->select(
                'episodes.id as episode_id',
                'seasons.season_number as season',
                'episodes.episode_number as episode',
                'episodes.source_url',
                'episodes.view_count'
            )
            ->orderBy('episodes.id');

        if ($onlyMissing) {
            $query->whereNotExists(function ($sub) {
                $sub->select(\DB::raw(1))
                    ->from('subtitles')
                    ->whereColumn('subtitles.episode_id', 'episodes.id');
            });
        } elseif ($fixAniziumSubs) {
            // Altyazısı var ama en az bir URL b-cdn.net içermiyor (ham Anizium linki)
            $query->whereExists(function ($sub) {
                $sub->select(\DB::raw(1))
                    ->from('subtitles')
                    ->whereColumn('subtitles.episode_id', 'episodes.id')
                    ->where('subtitles.url', 'not like', '%b-cdn.net%');
            });
        }

        $rows = $query->get();

        $grouped    = [];
        $viewCounts = [];
        foreach ($rows as $r) {
            if (!preg_match('#anizium\.co/watch/(\w+)#', $r->source_url, $m)) continue;
            $wid = $m[1];
            if (!isset($grouped[$wid])) {
                $grouped[$wid]    = [];
                $viewCounts[$wid] = 0;
            }
            $grouped[$wid][]     = [
                'episode_id' => $r->episode_id,
                'season'     => $r->season,
                'episode'    => $r->episode,
            ];
            $viewCounts[$wid] += (int) ($r->view_count ?? 0);
        }

        // Popularity'e göre sırala (en çok izlenen önce)
        $sortedAnimes = [];
        foreach ($grouped as $wid => $eps) {
            $sortedAnimes[$wid] = [
                'episodes'   => $eps,
                'view_count' => $viewCounts[$wid],
            ];
        }
        uasort($sortedAnimes, fn($a, $b) => $b['view_count'] - $a['view_count']);

        return response()->json([
            'total'  => $rows->count(),
            'animes' => $sortedAnimes,
        ]);
    }

    // Sağlık kontrolü: Anizium kaynaklı anime + bölüm URL'leri
    public function aniziumHealthData()
    {
        $rows = \DB::table('episodes')
            ->join('seasons',  'seasons.id',  '=', 'episodes.season_id')
            ->join('animes',   'animes.id',   '=', 'episodes.anime_id')
            ->join('import_jobs', function ($j) {
                $j->on('import_jobs.anime_id', '=', 'animes.id')
                  ->where('import_jobs.source', 'anizium')
                  ->where('import_jobs.status', 'done');
            })
            ->where('episodes.is_published', true)
            ->whereNotNull('episodes.source_url')
            ->where('episodes.source_url', 'like', '%anizium.co/watch/%')
            ->select(
                'animes.id   as anime_id',
                'animes.title',
                'animes.slug',
                'import_jobs.watch_id',
                'episodes.id as episode_id',
                'seasons.season_number as season',
                'episodes.episode_number as episode',
                'episodes.m3u8_url',
                'episodes.video_url'
            )
            ->orderBy('animes.id')
            ->orderBy('seasons.season_number')
            ->orderBy('episodes.episode_number')
            ->get();

        $animes = [];
        foreach ($rows as $r) {
            $aid = $r->anime_id;
            if (!isset($animes[$aid])) {
                $animes[$aid] = [
                    'anime_id' => $aid,
                    'title'    => $r->title,
                    'slug'     => $r->slug,
                    'watch_id' => $r->watch_id,
                    'episodes' => [],
                ];
            }
            $url = $r->m3u8_url ?: $r->video_url;
            if ($url) {
                $animes[$aid]['episodes'][] = [
                    'episode_id' => $r->episode_id,
                    'season'     => $r->season,
                    'episode'    => $r->episode,
                    'url'        => $url,
                ];
            }
        }

        return response()->json([
            'anime_count'   => count($animes),
            'episode_count' => $rows->count(),
            'animes'        => array_values($animes),
        ]);
    }

    // Anime'yi inaktife al (Anizium bozuk, yeniden import edilecek)
    public function deactivateAnime(Request $request)
    {
        $data = $request->validate(['anime_id' => 'required|integer|exists:animes,id']);

        Anime::where('id', $data['anime_id'])->update(['is_published' => false]);
        ImportJob::where('anime_id', $data['anime_id'])
                 ->where('source', 'anizium')
                 ->update(['status' => 'failed', 'error_log' => 'CDN URL broken — replaced']);

        return response()->json(['ok' => true]);
    }

    // Altyazıyı doğrudan episode_id ile kaydet
    public function saveSubtitleDirect(Request $request)
    {
        $data = $request->validate([
            'episode_id' => 'required|integer|exists:episodes,id',
            'language'   => 'required|string|max:10',
            'label'      => 'required|string|max:50',
            'url'        => 'required|string',
            'is_default' => 'boolean',
        ]);

        Subtitle::updateOrCreate(
            ['episode_id' => $data['episode_id'], 'language' => $data['language']],
            ['label' => $data['label'], 'url' => $data['url'], 'is_default' => $data['is_default'] ?? false]
        );

        return response()->json(['ok' => true]);
    }

    // ── Çapraz re-import: yayınlanan animeler için tamamlanmış job'ları döndür ─
    // Kullanım: cross_reimport.py scripti bu endpoint'i çağırır
    public function publishedAnimesWithJobs()
    {
        $animes = Anime::where('is_published', true)
            ->with(['importJobs' => fn($q) => $q->where('status', 'done')->select(
                'id', 'anime_id', 'source', 'watch_id', 'animecix_title_id', 'animecix_slug', 'status'
            )])
            ->select('id', 'title', 'title_en', 'slug', 'mal_id', 'type', 'status')
            ->get()
            ->map(function ($anime) {
                $jobs = $anime->importJobs;
                return [
                    'anime_id'   => $anime->id,
                    'title'      => $anime->title,
                    'title_en'   => $anime->title_en,
                    'slug'       => $anime->slug,
                    'mal_id'     => $anime->mal_id,
                    'type'       => $anime->type,
                    'status'     => $anime->status,
                    'has_anizium'   => $jobs->where('source', 'anizium')->isNotEmpty(),
                    'has_animecix'  => $jobs->where('source', 'animecix')->isNotEmpty(),
                    'anizium_watch_ids'       => $jobs->where('source', 'anizium')->pluck('watch_id')->filter()->unique()->values(),
                    'animecix_title_ids'      => $jobs->where('source', 'animecix')->pluck('animecix_title_id')->filter()->unique()->values(),
                    'animecix_slugs'          => $jobs->where('source', 'animecix')->pluck('animecix_slug')->filter()->unique()->values(),
                ];
            });

        return response()->json(['animes' => $animes, 'count' => $animes->count()]);
    }
}
