<?php
namespace App\Controllers;

use App\Core\{Auth, Csrf, Response};
use App\Core\Database;
use App\Models\{Comment, Music};
use Throwable;
use function App\Core\url;

class MusicController extends BaseController
{
    public function index(): string
    {
        try {
            $music = new Music();
            $featured = $music->featured(6);
            $latest = $music->latest(8);
            $popular = $music->popular(8);
            $genres = $music->genres();
            $artists = $music->featuredArtists(6);
            $heroTrack = $featured[0] ?? $popular[0] ?? $latest[0] ?? null;
            $sitePlans = $this->sitePlans();
        } catch (Throwable) {
            $fallback = $this->fallbackHomeData();
            $featured = $fallback['featured'];
            $latest = $fallback['latest'];
            $popular = $fallback['popular'];
            $genres = $fallback['genres'];
            $artists = $fallback['artists'];
            $heroTrack = $fallback['heroTrack'];
            $sitePlans = [];
        }

        return $this->view('music', 'music/index', [
            'title' => 'Zalorox Music',
            'heroTrack' => $heroTrack,
            'featured' => $featured,
            'latest' => $latest,
            'popular' => $popular,
            'genres' => $genres,
            'artists' => $artists,
            'sitePlans' => $sitePlans,
        ]);
    }

    public function show(string $slug): string
    {
        try {
            $music = new Music();
            $track = $music->findBySlug($slug);

            if (!$track) {
                return $this->notFound();
            }

            try {
                $music->recordView((int) $track['id']);
                $track['view_count']++;
            } catch (Throwable) {
                // View counters should never block the public track page.
            }

            $comments = (new Comment())->listForContent('music', (int) $track['id'], 40);
            $related = $music->related($track, 8);
            $canAccess = Auth::userCanAccessTrack($track);
            $userLiked = $music->userLiked((int) $track['id'], Auth::id());
            $canEdit = $this->canEdit($track);

            return $this->view('music', 'music/show', [
                'title' => $track['title'],
                'track' => $track,
                'comments' => $comments,
                'related' => $related,
                'canAccess' => $canAccess,
                'userLiked' => $userLiked,
                'canEdit' => $canEdit,
                'sitePlans' => $this->sitePlans(),
            ]);
        } catch (Throwable) {
            $track = null;
            foreach ($this->fallbackTracks() as $item) {
                if (($item['slug'] ?? '') === $slug) {
                    $track = $item;
                    break;
                }
            }

            if (!$track) {
                return $this->notFound();
            }

            return $this->view('music', 'music/show', [
                'title' => $track['title'],
                'track' => $track,
                'comments' => [],
                'related' => array_values(array_filter($this->fallbackTracks(), static fn (array $item): bool => ($item['slug'] ?? '') !== $slug)),
                'canAccess' => empty($track['premium']),
                'userLiked' => false,
                'canEdit' => false,
                'sitePlans' => [],
            ]);
        }
    }

    public function artist(string $slug): string
    {
        try {
            $music = new Music();
            $artist = $music->findArtistBySlug($slug);
            if (!$artist) {
                return $this->notFound('Sanatci bulunamadi', 'Aradigin sanatci profili henuz yayinda degil.');
            }

            return $this->view('music', 'music/artist', [
                'title' => ($artist['artist_name'] ?? 'Sanatci') . ' - Zalorox Music',
                'artist' => $artist,
                'tracks' => $music->tracksForArtist($artist, 40),
            ]);
        } catch (Throwable $exception) {
            $this->logException($exception, 'music.artist');

            return $this->notFound('Sanatci bulunamadi', 'Artist altyapisi icin migration calistirilmis olmali.');
        }
    }

    public function genre(string $slug): string
    {
        $sort = $this->normalizeSort((string) ($_GET['sirala'] ?? $_GET['sort'] ?? 'newest'));

        try {
            $music = new Music();
            $genre = $music->genreBySlug($slug);

            if (!$genre) {
                return $this->notFound('Tarz bulunamadi', 'Aradigin muzik tarzi henuz yayinda degil.');
            }

            $tracks = $music->list([
                'genre_slug' => $slug,
                'sort' => $sort,
                'limit' => 36,
            ]);
            $genres = $music->genres();
        } catch (Throwable) {
            $genre = $this->fallbackGenre($slug);
            $tracks = array_values(array_filter($this->fallbackTracks(), static fn (array $track): bool => ($track['genre_slug'] ?? '') === $slug));
            $genres = $this->fallbackGenres();
        }

        return $this->view('music', 'music/genre', [
            'title' => $genre['name'] . ' - Zalorox Music',
            'genre' => $genre,
            'genres' => $genres,
            'tracks' => $tracks,
            'sort' => $sort,
        ]);
    }

    public function search(): string
    {
        $q = trim((string) ($_GET['q'] ?? ''));
        $sort = $this->normalizeSort((string) ($_GET['sirala'] ?? $_GET['sort'] ?? 'newest'));

        try {
            $tracks = (new Music())->list([
                'q' => $q,
                'sort' => $sort,
                'limit' => 36,
            ]);
        } catch (Throwable) {
            $tracks = $this->fallbackSearch($q, $sort);
        }

        return $this->view('music', 'music/list', [
            'title' => $q === '' ? 'Music Arama' : 'Music Arama: ' . $q,
            'heading' => $q === '' ? 'Muzik Arama' : 'Arama: ' . $q,
            'tracks' => $tracks,
            'q' => $q,
            'sort' => $sort,
        ]);
    }

    public function searchApi(): Response
    {
        if (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST' && !Csrf::verify((string) $this->input('_csrf', ''))) {
            return Response::json(['ok' => false, 'message' => 'Guvenlik oturumu yenilenmeli.'], 419);
        }

        $q = trim((string) $this->input('q', $_GET['q'] ?? ''));
        $sort = $this->normalizeSort((string) $this->input('sort', $_GET['sirala'] ?? $_GET['sort'] ?? 'newest'));

        try {
            $tracks = (new Music())->list([
                'q' => $q,
                'sort' => $sort,
                'limit' => 12,
            ]);

            return Response::json(['ok' => true, 'tracks' => $tracks]);
        } catch (Throwable) {
            return Response::json(['ok' => true, 'tracks' => $this->fallbackSearch($q, $sort)]);
        }
    }

    public function queueApi(): Response
    {
        $trackId = (int) ($_GET['track_id'] ?? $this->input('track_id', 0));
        if ($trackId <= 0) {
            return Response::json(['ok' => false, 'message' => 'Sarki hedefi gecersiz.', 'queue' => []], 422);
        }

        try {
            $music = new Music();
            $track = $music->findPublishedById($trackId);

            if (!$track) {
                return Response::json(['ok' => false, 'message' => 'Sarki bulunamadi.', 'queue' => []], 404);
            }

            $queue = $music->related($track, 12);
            if (count($queue) < 12) {
                $seen = array_merge(
                    [(int) ($track['id'] ?? 0)],
                    array_map(static fn (array $item): int => (int) ($item['id'] ?? 0), $queue)
                );
                $queue = array_merge($queue, $music->list([
                    'limit' => 12 - count($queue),
                    'sort' => 'newest',
                    'exclude_ids' => $seen,
                ]));
            }

            return Response::json([
                'ok' => true,
                'strategy' => ($track['genre_slug'] ?? '') !== '' ? 'genre' : 'popular',
                'track' => $this->queueTrackPayload($track),
                'queue' => array_map(fn (array $item): array => $this->queueTrackPayload($item), array_slice($queue, 0, 12)),
            ]);
        } catch (Throwable) {
            return Response::json([
                'ok' => true,
                'strategy' => 'fallback',
                'track' => null,
                'queue' => $this->fallbackQueue($trackId),
            ]);
        }
    }

    public function recommendationsApi(): Response
    {
        return $this->queueApi();
    }

    public function playApi(): Response
    {
        if (!Csrf::verify((string) $this->input('_csrf', ''))) {
            return Response::json(['ok' => false, 'message' => 'Guvenlik oturumu yenilenmeli.'], 419);
        }

        $trackId = (int) $this->input('track_id', 0);
        if ($trackId <= 0) {
            return Response::json(['ok' => false, 'message' => 'Sarki hedefi gecersiz.'], 422);
        }

        try {
            $music = new Music();
            $track = $music->findPublishedById($trackId);

            if (!$track) {
                return Response::json(['ok' => false, 'message' => 'Sarki bulunamadi.'], 404);
            }

            if (!Auth::userCanAccessTrack($track)) {
                return Response::json(['ok' => false, 'message' => 'Bu premium sarkiyi dinlemek icin site premium aboneligi gerekli.'], 403);
            }

            $result = $music->recordPlay((int) $track['id'], Auth::id(), $_SERVER['REMOTE_ADDR'] ?? null);

            return Response::json(['ok' => true] + $result);
        } catch (Throwable) {
            return Response::json(['ok' => false, 'message' => 'Dinlenme su anda kaydedilemedi.'], 500);
        }
    }

    public function likeApi(): Response
    {
        if (!Auth::check()) {
            return Response::json(['ok' => false, 'message' => 'Begenmek icin giris yapmalisin.'], 403);
        }

        if (!Csrf::verify((string) $this->input('_csrf', ''))) {
            return Response::json(['ok' => false, 'message' => 'Guvenlik oturumu yenilenmeli.'], 419);
        }

        $trackId = (int) $this->input('track_id', 0);
        if ($trackId <= 0) {
            return Response::json(['ok' => false, 'message' => 'Sarki hedefi gecersiz.'], 422);
        }

        try {
            $music = new Music();
            $track = $music->findPublishedById($trackId);

            if (!$track) {
                return Response::json(['ok' => false, 'message' => 'Sarki bulunamadi.'], 404);
            }

            $result = $music->toggleLike((int) $track['id'], (int) Auth::id());

            return Response::json(['ok' => true] + $result);
        } catch (Throwable) {
            return Response::json(['ok' => false, 'message' => 'Begeni su anda kaydedilemedi.'], 500);
        }
    }

    public function shareTrackApi(): Response
    {
        if (!Csrf::verify((string) $this->input('_csrf', ''))) {
            return Response::json(['ok' => false, 'message' => 'Guvenlik oturumu yenilenmeli.'], 419);
        }

        $trackId = (int) $this->input('track_id', 0);
        if ($trackId <= 0) {
            return Response::json(['ok' => false, 'message' => 'Sarki hedefi gecersiz.'], 422);
        }

        try {
            $track = (new Music())->findPublishedById($trackId);

            if (!$track) {
                return Response::json(['ok' => false, 'message' => 'Sarki bulunamadi.'], 404);
            }

            return Response::json([
                'ok' => true,
                'title' => $track['title'] . ' - ' . $track['artist_name'],
                'url' => url('/music/sarki/' . $track['slug']),
            ]);
        } catch (Throwable) {
            return Response::json(['ok' => false, 'message' => 'Paylasim bilgisi hazirlanamadi.'], 500);
        }
    }

    private function input(string $key, mixed $default = null): mixed
    {
        static $json = null;

        if (array_key_exists($key, $_POST)) {
            return $_POST[$key];
        }

        if ($json === null) {
            $raw = file_get_contents('php://input') ?: '';
            $decoded = json_decode($raw, true);
            $json = is_array($decoded) ? $decoded : [];
        }

        return $json[$key] ?? $default;
    }

    private function canEdit(array $track): bool
    {
        $user = Auth::user();
        if (!$user) {
            return false;
        }

        return (string) ($user['role'] ?? '') === 'admin'
            || (int) ($user['id'] ?? 0) === (int) ($track['user_id'] ?? 0);
    }

    private function normalizeSort(string $sort): string
    {
        return match ($sort) {
            'populer', 'popular' => 'popular',
            'dinlenen', 'plays', 'listened', 'en-cok-dinlenen' => 'plays',
            'premium' => 'premium',
            'ucretsiz', 'free' => 'free',
            default => 'newest',
        };
    }

    private function fallbackHomeData(): array
    {
        $tracks = $this->fallbackTracks();
        $popular = $tracks;
        usort($popular, static fn (array $a, array $b): int => ((int) ($b['play_count'] ?? 0)) <=> ((int) ($a['play_count'] ?? 0)));

        return [
            'heroTrack' => $tracks[0] ?? null,
            'featured' => array_values(array_filter($tracks, static fn (array $track): bool => !empty($track['featured']))),
            'latest' => $tracks,
            'popular' => $popular,
            'genres' => $this->fallbackGenres(),
            'artists' => $this->fallbackArtists($tracks),
        ];
    }

    private function fallbackTracks(): array
    {
        return array_map(function (array $track): array {
            $track['artist_name'] = $track['artist_name'] ?? $track['artist'] ?? 'Zalorox Music';
            $track['cover_image'] = $track['cover_image'] ?? $track['cover'] ?? '/uploads/music-covers/track-1.svg';
            $track['audio_file'] = $track['audio_file'] ?? '';
            $track['privacy'] = $track['privacy'] ?? (empty($track['premium']) ? 'public' : 'premium');
            $track['play_count'] = (int) ($track['play_count'] ?? $track['plays'] ?? 0);
            $track['like_count'] = (int) ($track['like_count'] ?? $track['likes'] ?? 0);
            $track['view_count'] = (int) ($track['view_count'] ?? 0);
            $track['featured'] = (int) ($track['featured'] ?? 0);
            $track['genre'] = $track['genre'] ?? 'Music';
            $track['genre_slug'] = $track['genre_slug'] ?? 'music';
            $track['genre_names'] = $track['genre_names'] ?? $track['genre'];
            $track['genre_slugs'] = $track['genre_slugs'] ?? $track['genre_slug'];
            $track['is_premium'] = in_array($track['privacy'], ['premium', 'subscriber', 'private'], true);
            $track['premium'] = $track['is_premium'];
            $track['youtube_url'] = $track['youtube_url'] ?? '';
            $track['spotify_url'] = $track['spotify_url'] ?? '';
            $track['apple_music_url'] = $track['apple_music_url'] ?? '';
            $track['external_link'] = $track['external_link'] ?? '';
            $track['description'] = $track['description'] ?? 'Demo muzik kaydi.';

            return $track;
        }, $this->samples()['music'] ?? []);
    }

    private function fallbackSearch(string $q, string $sort): array
    {
        $tracks = $this->fallbackTracks();

        if ($q !== '') {
            $needle = strtolower($q);
            $tracks = array_values(array_filter($tracks, static function (array $track) use ($needle): bool {
                return str_contains(strtolower((string) ($track['title'] ?? '')), $needle)
                    || str_contains(strtolower((string) ($track['artist_name'] ?? '')), $needle)
                    || str_contains(strtolower((string) ($track['description'] ?? '')), $needle)
                    || str_contains(strtolower((string) ($track['genre'] ?? '')), $needle);
            }));
        }

        usort($tracks, static function (array $a, array $b) use ($sort): int {
            return match ($sort) {
                'popular', 'plays' => ((int) ($b['play_count'] ?? 0)) <=> ((int) ($a['play_count'] ?? 0)),
                'premium' => ((int) !empty($b['premium'])) <=> ((int) !empty($a['premium'])),
                'free' => ((int) empty($b['premium'])) <=> ((int) empty($a['premium'])),
                default => ((int) ($b['id'] ?? 0)) <=> ((int) ($a['id'] ?? 0)),
            };
        });

        return $tracks;
    }

    private function fallbackQueue(int $trackId): array
    {
        $tracks = $this->fallbackTracks();
        $current = null;

        foreach ($tracks as $track) {
            if ((int) ($track['id'] ?? 0) === $trackId) {
                $current = $track;
                break;
            }
        }

        $genreSlug = (string) ($current['genre_slug'] ?? '');
        $queue = [];
        if ($genreSlug !== '') {
            $queue = array_values(array_filter($tracks, static function (array $track) use ($trackId, $genreSlug): bool {
                return (int) ($track['id'] ?? 0) !== $trackId && (string) ($track['genre_slug'] ?? '') === $genreSlug;
            }));
        }

        if (count($queue) < 12) {
            $seen = array_merge([$trackId], array_map(static fn (array $track): int => (int) ($track['id'] ?? 0), $queue));
            $popular = array_values(array_filter($tracks, static fn (array $track): bool => !in_array((int) ($track['id'] ?? 0), $seen, true)));
            usort($popular, static fn (array $a, array $b): int => ((int) ($b['play_count'] ?? 0)) <=> ((int) ($a['play_count'] ?? 0)));
            $queue = array_merge($queue, $popular);
        }

        return array_map(fn (array $track): array => $this->queueTrackPayload($track), array_slice($queue, 0, 12));
    }

    private function queueTrackPayload(array $track): array
    {
        $cover = (string) (($track['cover_image'] ?? $track['cover'] ?? '') ?: '/uploads/music-covers/track-1.svg');
        $audio = (string) (($track['audio_file'] ?? $track['audio'] ?? '') ?: '');
        $audioSrc = (str_starts_with($audio, '/uploads/') || str_starts_with($audio, '/assets/')) ? url($audio) : '';
        $slug = (string) ($track['slug'] ?? '');

        return [
            'id' => (int) ($track['id'] ?? 0),
            'track_id' => (int) ($track['id'] ?? 0),
            'title' => (string) (($track['title'] ?? '') ?: 'Untitled Track'),
            'artist' => (string) (($track['artist_name'] ?? $track['artist'] ?? '') ?: 'Zalorox Music'),
            'cover' => url($cover),
            'audio_src' => $audioSrc,
            'detail_url' => $slug !== '' ? url('/music/sarki/' . $slug) : url('/music'),
            'genre' => (string) (($track['genre'] ?? '') ?: 'Music'),
            'genre_slug' => (string) (($track['genre_slug'] ?? '') ?: 'music'),
            'locked' => !Auth::userCanAccessTrack($track),
            'premium' => !empty($track['premium']) || !empty($track['is_premium']),
            'play_endpoint' => url('/api/music/play'),
        ];
    }

    private function fallbackGenres(): array
    {
        return [
            ['id' => 0, 'name' => 'Rap', 'slug' => 'rap', 'description' => 'Zalorox rap secimleri.', 'icon' => 'RP', 'banner' => '/uploads/music-covers/track-1.svg', 'track_count' => 0],
            ['id' => 0, 'name' => 'Arabesk Rap', 'slug' => 'arabesk-rap', 'description' => 'Arabesk rap demo listesi.', 'icon' => 'AR', 'banner' => '/uploads/music-covers/track-2.svg', 'track_count' => 0],
            ['id' => 0, 'name' => 'Phonk', 'slug' => 'phonk', 'description' => 'Phonk ve drift hissi.', 'icon' => 'PH', 'banner' => '/uploads/music-covers/track-1.svg', 'track_count' => 0],
            ['id' => 0, 'name' => 'Turku', 'slug' => 'turku', 'description' => 'Modern turku kayitlari.', 'icon' => 'TK', 'banner' => '/uploads/music-covers/track-2.svg', 'track_count' => 0],
            ['id' => 0, 'name' => 'Karadeniz', 'slug' => 'karadeniz', 'description' => 'Karadeniz esintili parcalar.', 'icon' => 'KD', 'banner' => '/uploads/music-covers/track-1.svg', 'track_count' => 0],
            ['id' => 0, 'name' => 'Remix', 'slug' => 'remix', 'description' => 'Remix demo kayitlari.', 'icon' => 'RX', 'banner' => '/uploads/music-covers/track-2.svg', 'track_count' => 0],
            ['id' => 0, 'name' => 'Trap', 'slug' => 'trap', 'description' => 'Trap altyapilar.', 'icon' => 'TR', 'banner' => '/uploads/music-covers/track-1.svg', 'track_count' => 0],
            ['id' => 0, 'name' => 'Oyun Muzigi', 'slug' => 'oyun-muzigi', 'description' => 'Oyun ve sinematik parcalar.', 'icon' => 'OM', 'banner' => '/uploads/music-covers/track-2.svg', 'track_count' => 0],
        ];
    }

    private function fallbackGenre(string $slug): array
    {
        foreach ($this->fallbackGenres() as $genre) {
            if ($genre['slug'] === $slug) {
                return $genre;
            }
        }

        return ['id' => 0, 'name' => 'Music', 'slug' => $slug, 'description' => '', 'icon' => '', 'banner' => '', 'track_count' => 0];
    }

    private function fallbackArtists(array $tracks): array
    {
        $artists = [];
        foreach ($tracks as $track) {
            $name = (string) ($track['artist_name'] ?? 'Zalorox Music');
            $artists[$name] ??= ['name' => $name, 'slug' => $this->slugify($name), 'track_count' => 0, 'play_count' => 0, 'like_count' => 0, 'cover_image' => $track['cover_image'] ?? '/uploads/music-covers/track-1.svg'];
            $artists[$name]['track_count']++;
            $artists[$name]['play_count'] += (int) ($track['play_count'] ?? 0);
            $artists[$name]['like_count'] += (int) ($track['like_count'] ?? 0);
        }

        return array_values($artists);
    }

    private function sitePlans(): array
    {
        try {
            $statement = Database::connection($this->config)->query(
                'SELECT * FROM site_plans WHERE status = "active" ORDER BY price_monthly ASC, id ASC LIMIT 6'
            );

            return array_map(static function (array $plan): array {
                $features = json_decode((string) ($plan['features_json'] ?? '[]'), true);
                $plan['features'] = is_array($features) ? $features : [];

                return $plan;
            }, $statement->fetchAll());
        } catch (Throwable) {
            return [];
        }
    }

    private function slugify(string $value): string
    {
        $value = strtolower(trim($value));
        $value = strtr($value, [
            'ç' => 'c', 'ğ' => 'g', 'ı' => 'i', 'ö' => 'o', 'ş' => 's', 'ü' => 'u',
            'Ç' => 'c', 'Ğ' => 'g', 'İ' => 'i', 'Ö' => 'o', 'Ş' => 's', 'Ü' => 'u',
        ]);
        $value = preg_replace('~[^a-z0-9]+~', '-', $value) ?: '';
        $value = trim($value, '-');

        return $value !== '' ? substr($value, 0, 160) : 'artist';
    }

    private function notFound(string $title = 'Sarki bulunamadi', string $message = 'Aradigin sarki yayinda degil veya tasinmis olabilir.'): string
    {
        http_response_code(404);

        return $this->view('music', 'pages/show', [
            'title' => '404',
            'page' => [
                'title' => $title,
                'content' => $message,
            ],
        ]);
    }
}
