<?php

namespace App\Services;

use App\Enums\VideoProvider;
use InvalidArgumentException;

class VideoUrlParserService
{
    private const ALLOWED_HOSTS = [
        'youtube.com',
        'www.youtube.com',
        'm.youtube.com',
        'youtu.be',
        'www.youtu.be',
        'twitch.tv',
        'www.twitch.tv',
        'clips.twitch.tv',
        'vimeo.com',
        'www.vimeo.com',
        'player.vimeo.com',
    ];

    public function parse(string $input): array
    {
        $input = trim($input);

        if ($input === '') {
            throw new InvalidArgumentException(__('validation.video_url_invalid'));
        }

        $this->rejectUnsafeInput($input);

        $url = $this->normalizeInputToUrl($input);
        $host = strtolower(parse_url($url, PHP_URL_HOST) ?? '');

        if (! $this->isAllowedHost($host)) {
            throw new InvalidArgumentException(__('validation.video_url_invalid'));
        }

        return match (true) {
            $this->isYouTubeHost($host) => $this->parseYouTube($url),
            $this->isTwitchHost($host) => $this->parseTwitch($url),
            $this->isVimeoHost($host) => $this->parseVimeo($url),
            default => throw new InvalidArgumentException(__('validation.video_url_invalid')),
        };
    }

    private function rejectUnsafeInput(string $input): void
    {
        if (preg_match('/<\s*(iframe|script|object|embed|html|body)/i', $input)) {
            throw new InvalidArgumentException(__('validation.video_url_invalid'));
        }

        if (str_contains($input, '<') || str_contains($input, '>')) {
            throw new InvalidArgumentException(__('validation.video_url_invalid'));
        }
    }

    private function normalizeInputToUrl(string $input): string
    {
        if (! str_contains($input, '://')) {
            $input = 'https://'.$input;
        }

        if (! filter_var($input, FILTER_VALIDATE_URL)) {
            throw new InvalidArgumentException(__('validation.video_url_invalid'));
        }

        return $input;
    }

    private function isAllowedHost(string $host): bool
    {
        foreach (self::ALLOWED_HOSTS as $allowed) {
            if ($host === $allowed || str_ends_with($host, '.'.$allowed)) {
                return true;
            }
        }

        return false;
    }

    private function isYouTubeHost(string $host): bool
    {
        return in_array($host, ['youtube.com', 'www.youtube.com', 'm.youtube.com', 'youtu.be', 'www.youtu.be'], true);
    }

    private function isTwitchHost(string $host): bool
    {
        return in_array($host, ['twitch.tv', 'www.twitch.tv', 'clips.twitch.tv'], true);
    }

    private function isVimeoHost(string $host): bool
    {
        return in_array($host, ['vimeo.com', 'www.vimeo.com', 'player.vimeo.com'], true);
    }

    private function parseYouTube(string $url): array
    {
        $parts = parse_url($url);
        $host = strtolower($parts['host'] ?? '');
        $videoId = null;

        if (in_array($host, ['youtu.be', 'www.youtu.be'], true)) {
            $videoId = trim($parts['path'] ?? '', '/');
        } else {
            parse_str($parts['query'] ?? '', $query);
            $videoId = $query['v'] ?? null;

            if (! $videoId && str_starts_with($parts['path'] ?? '', '/embed/')) {
                $videoId = trim(str_replace('/embed/', '', $parts['path']), '/');
            }

            if (! $videoId && str_starts_with($parts['path'] ?? '', '/shorts/')) {
                $videoId = trim(str_replace('/shorts/', '', $parts['path']), '/');
            }
        }

        if (! $this->isValidVideoId($videoId)) {
            throw new InvalidArgumentException(__('validation.video_url_invalid'));
        }

        $normalized = 'https://www.youtube.com/watch?v='.$videoId;

        return [
            'provider' => VideoProvider::YouTube,
            'external_video_id' => $videoId,
            'normalized_url' => $normalized,
            'safe_embed_url' => 'https://www.youtube-nocookie.com/embed/'.$videoId,
            'thumbnail_url' => 'https://i.ytimg.com/vi/'.$videoId.'/hqdefault.jpg',
        ];
    }

    private function parseTwitch(string $url): array
    {
        $parts = parse_url($url);
        $path = trim($parts['path'] ?? '', '/');
        $segments = array_values(array_filter(explode('/', $path)));

        if (count($segments) >= 2 && $segments[0] === 'videos') {
            $videoId = $segments[1];

            if (! $this->isValidVideoId($videoId)) {
                throw new InvalidArgumentException(__('validation.video_url_invalid'));
            }

            return [
                'provider' => VideoProvider::Twitch,
                'external_video_id' => $videoId,
                'normalized_url' => 'https://www.twitch.tv/videos/'.$videoId,
                'safe_embed_url' => 'https://player.twitch.tv/?video='.$videoId.'&parent='.parse_url(config('app.url'), PHP_URL_HOST),
                'thumbnail_url' => null,
            ];
        }

        if (($parts['host'] ?? '') === 'clips.twitch.tv' && count($segments) >= 1) {
            $clipId = $segments[0];

            return [
                'provider' => VideoProvider::Twitch,
                'external_video_id' => $clipId,
                'normalized_url' => 'https://clips.twitch.tv/'.$clipId,
                'safe_embed_url' => 'https://clips.twitch.tv/embed?clip='.$clipId.'&parent='.parse_url(config('app.url'), PHP_URL_HOST),
                'thumbnail_url' => null,
            ];
        }

        throw new InvalidArgumentException(__('validation.video_url_invalid'));
    }

    private function parseVimeo(string $url): array
    {
        $parts = parse_url($url);
        $path = trim($parts['path'] ?? '', '/');
        $segments = array_values(array_filter(explode('/', $path)));
        $videoId = null;

        if (str_starts_with($path, 'video/')) {
            $videoId = str_replace('video/', '', $path);
        } elseif (count($segments) >= 1 && ctype_digit($segments[0])) {
            $videoId = $segments[0];
        }

        if (! $this->isValidVideoId($videoId)) {
            throw new InvalidArgumentException(__('validation.video_url_invalid'));
        }

        return [
            'provider' => VideoProvider::Vimeo,
            'external_video_id' => $videoId,
            'normalized_url' => 'https://vimeo.com/'.$videoId,
            'safe_embed_url' => 'https://player.vimeo.com/video/'.$videoId,
            'thumbnail_url' => null,
        ];
    }

    private function isValidVideoId(?string $id): bool
    {
        return filled($id) && preg_match('/^[A-Za-z0-9_-]{3,64}$/', $id) === 1;
    }
}
