<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use App\Models\TelegramUser;
use App\Models\TelegramRequiredChat;
use App\Models\TelegramSetting;
use App\Models\TelegramAd;
use Telegram\Bot\Exceptions\TelegramResponseException;
use Telegram\Bot\Laravel\Facades\Telegram;
use Telegram\Bot\FileUpload\InputFile;
use App\Services\YtDlpDownloader;

class TelegramWebhookController extends Controller
{
    public function handle(Request $request, YtDlpDownloader $downloader)
    {
        try {
            Log::info('telegram.webhook: incoming', [
                'ip' => $request->ip(),
                'content_type' => $request->header('content-type'),
                'has_body' => $request->getContent() !== '',
            ]);
            $update = Telegram::getWebhookUpdates();

            Log::info('telegram.webhook: update received', [
                'has_callback' => (bool) $update->getCallbackQuery(),
                'has_message' => (bool) $update->getMessage(),
            ]);

            $callback = $update->getCallbackQuery();
            if ($callback) {
                $chatId = $callback->getMessage()?->getChat()?->getId();
                $data = trim((string) $callback->getData());

                if ($chatId) {
                    $this->upsertUserFromUpdate($update);
                    Telegram::answerCallbackQuery([
                        'callback_query_id' => $callback->getId(),
                    ]);

                    if (preg_match('~^dl:(\d{3,4}):([A-Za-z0-9_-]{8,64})$~', $data, $m)) {
                        $required = $this->requiredChatsForPrompt();
                        $userId = (int) ($callback->getFrom()?->getId() ?? 0);
                        if ($userId <= 0) {
                            $userId = (int) $chatId;
                        }

                        if ($this->mustRequireSubscription((int) $chatId) && !$this->isSubscribed($userId, (int) $chatId)) {
                            Telegram::sendMessage([
                                'chat_id' => $chatId,
                                'text' => $required !== ''
                                    ? "Чтобы скачать видео, подпишись и попробуй снова:\n\n{$required}"
                                    : 'Чтобы скачать видео, подпишись на канал/группу и попробуй снова.',
                            ]);
                            return response('ok', 200);
                        }

                        $height = (int) $m[1];
                        $token = (string) $m[2];
                        $cacheKey = $this->cacheKey($token);
                        $url = Cache::get($cacheKey);
                        Log::info('telegram.webhook: dl callback', [
                            'chat_id' => $chatId,
                            'height' => $height,
                            'cache_key' => $cacheKey,
                            'has_url' => (bool) $url,
                        ]);
                        $maxUploadMb = (int) env('TELEGRAM_MAX_UPLOAD_MB', 50);
                        $maxUploadBytes = $maxUploadMb > 0 ? ($maxUploadMb * 1024 * 1024) : (50 * 1024 * 1024);
                        $maxDownloadMb = (int) env('TELEGRAM_MAX_DOWNLOAD_MB', 200);
                        $maxDownloadBytes = $maxDownloadMb > 0 ? ($maxDownloadMb * 1024 * 1024) : (200 * 1024 * 1024);

                        if (!$url) {
                            Telegram::sendMessage([
                                'chat_id' => $chatId,
                                'text' => 'Ссылка не найдена. Пришли URL заново.',
                            ]);

                            return response('ok', 200);
                        }

                        $estimated = $downloader->estimateMp4Size($url, $height);
                        Log::info('telegram.webhook: size estimate', [
                            'chat_id' => $chatId,
                            'height' => $height,
                            'estimated_bytes' => $estimated,
                            'limit_bytes' => $maxDownloadBytes,
                        ]);
                        if (is_int($estimated) && $estimated > $maxDownloadBytes) {
                            $mb = round($estimated / 1024 / 1024, 1);
                            Telegram::sendMessage([
                                'chat_id' => $chatId,
                                'text' => "Файл слишком большой (~{$mb} MB) — лимит скачивания {$maxDownloadMb}MB. Не скачиваю.",
                            ]);

                            return response('ok', 200);
                        }

                        Telegram::sendMessage([
                            'chat_id' => $chatId,
                            'text' => "Скачиваю... ⏳ ({$height}p)",
                        ]);

                        $result = $downloader->downloadMp4($url, $height);

                        if (!$result['ok']) {
                            $tail = implode("\n", $result['log_tail'] ?? []);
                            $extra = (($result['code'] ?? null) === 0)
                                ? "\n\nКод 0 = yt-dlp отработал, но файл не найден/не определён."
                                : "";

                            Telegram::sendMessage([
                                'chat_id' => $chatId,
                                'text' => "Ошибка ❌ ({$height}p)\nКод: {$result['code']}{$extra}\n\n{$tail}",
                            ]);

                            return response('ok', 200);
                        }

                        $filePath = $result['file'];
                        $size = @filesize($filePath);

                        if ($size !== false && $size <= $maxUploadBytes) {
                            $sent = false;
                            try {
                                Telegram::sendVideo([
                                    'chat_id' => $chatId,
                                    'video' => InputFile::create($filePath, basename($filePath)),
                                    'caption' => "Готово ✅ ({$height}p)",
                                    'supports_streaming' => true,
                                ]);
                                $sent = true;
                            } catch (TelegramResponseException $e) {
                                if (($e->getCode() ?? 0) === 413 || str_contains($e->getMessage(), 'Request Entity Too Large')) {
                                    $sent = false;
                                } else {
                                    try {
                                        Telegram::sendDocument([
                                            'chat_id' => $chatId,
                                            'document' => InputFile::create($filePath, basename($filePath)),
                                            'caption' => "Видео отправил файлом ✅ ({$height}p)",
                                        ]);
                                        $sent = true;
                                    } catch (\Throwable $e2) {
                                        $sent = false;
                                    }
                                }
                            } catch (\Throwable $e) {
                                try {
                                    Telegram::sendDocument([
                                        'chat_id' => $chatId,
                                        'document' => InputFile::create($filePath, basename($filePath)),
                                        'caption' => "Видео отправил файлом ✅ ({$height}p)",
                                    ]);
                                    $sent = true;
                                } catch (\Throwable $e2) {
                                    $sent = false;
                                }
                            }

                            if (!$sent) {
                                $publicName = 'videos/' . Str::uuid()->toString() . '.mp4';
                                $publicDiskPath = storage_path('app/public/' . $publicName);
                                @mkdir(dirname($publicDiskPath), 0777, true);
                                @rename($filePath, $publicDiskPath);

                                $publicUrl = url('/storage/app/videos/' . basename($publicName));
                                $mb = $size ? round($size / 1024 / 1024, 1) : '?';
                                Telegram::sendMessage([
                                    'chat_id' => $chatId,
                                    'text' => "Не смог отправить файл ({$mb} MB) через Telegram. Ссылка для скачивания:\n{$publicUrl}",
                                ]);

                                $this->incrementDownloads((int) $chatId);
                                $this->maybeSendAd((int) $chatId);

                                return response('ok', 200);
                            }

                            $this->incrementDownloads((int) $chatId);
                            $this->maybeSendAd((int) $chatId);

                            if (is_file($filePath)) {
                                @unlink($filePath);
                            }

                            return response('ok', 200);
                        }

                        $publicName = 'videos/' . Str::uuid()->toString() . '.mp4';
                        $publicDiskPath = storage_path('app/public/' . $publicName);
                        @mkdir(dirname($publicDiskPath), 0777, true);

                        $moved = @rename($filePath, $publicDiskPath);
                        if (!$moved) {
                            $moved = @copy($filePath, $publicDiskPath);
                            if ($moved && is_file($filePath)) {
                                @unlink($filePath);
                            }
                        }

                        if ($moved) {
                            $publicUrl = url('/storage/app/videos/' . basename($publicName));
                            $mb = $size ? round($size / 1024 / 1024, 1) : '?';
                            Telegram::sendMessage([
                                'chat_id' => $chatId,
                                'text' => "Файл большой для Telegram ({$mb} MB). Скачать по ссылке:\n{$publicUrl}",
                            ]);

                            $this->incrementDownloads((int) $chatId);
                            $this->maybeSendAd((int) $chatId);
                            return response('ok', 200);
                        }

                        $mb = $size ? round($size / 1024 / 1024, 1) : '?';
                        Telegram::sendMessage([
                            'chat_id' => $chatId,
                            'text' => "Файл большой ({$mb} MB) и не удалось сохранить его для ссылки.",
                        ]);

                        if (is_file($filePath)) {
                            @unlink($filePath);
                        }

                        return response('ok', 200);
                    }
                }
            }

            $message = $update->getMessage();
            $chatId = $message?->getChat()?->getId();
            $text = trim((string) ($message?->getText() ?? ''));

            if (!$chatId) {
                return response('ok', 200);
            }

            $this->upsertUserFromUpdate($update);

            $contact = $message?->getContact();
            if ($contact) {
                $this->upsertUserPhone((int) $chatId, (string) ($contact->getPhoneNumber() ?? ''));
                Telegram::sendMessage([
                    'chat_id' => $chatId,
                    'text' => 'Телефон сохранён ✅',
                ]);
                return response('ok', 200);
            }

            if ($text === '/users' || $text === 'users') {
                $total = TelegramUser::query()->count();
                Telegram::sendMessage([
                    'chat_id' => $chatId,
                    'text' => "Пользователей: {$total}",
                ]);

                return response('ok', 200);
            }

            if (!preg_match('~https?://\S+~', $text, $m)) {
                Telegram::sendMessage([
                    'chat_id' => $chatId,
                    'text' => 'Пришли ссылку на видео (YouTube/Instagram и т.д.)',
                ]);

                return response('ok', 200);
            }

            $url = $m[0];

            $token = $this->makeToken();
            Cache::put($this->cacheKey($token), $url, now()->addMinutes(30));

            Telegram::sendMessage([
                'chat_id' => $chatId,
                'text' => "Принял ссылку:\n{$url}",
            ]);

            Telegram::sendMessage([
                'chat_id' => $chatId,
                'text' => "Выбери качество:",
                'reply_markup' => json_encode([
                    'inline_keyboard' => [
                        [
                            ['text' => '1080p', 'callback_data' => "dl:1080:{$token}"],
                            ['text' => '720p', 'callback_data' => "dl:720:{$token}"],
                            ['text' => '480p', 'callback_data' => "dl:480:{$token}"],
                            ['text' => '360p', 'callback_data' => "dl:360:{$token}"],
                        ],
                    ],
                ], JSON_UNESCAPED_UNICODE),
            ]);

            return response('ok', 200);
        } catch (\Throwable $e) {
            Log::error('telegram.webhook: unhandled exception', [
                'message' => $e->getMessage(),
            ]);
            return response('ok', 200);
        }
    }

    private function cacheKey(string $token): string
    {
        return 'tg:url:' . $token;
    }

    private function makeToken(): string
    {
        $bytes = random_bytes(18);
        $b64 = rtrim(strtr(base64_encode($bytes), '+/', '-_'), '=');
        return $b64;
    }

    private function upsertUserFromUpdate($update): void
    {
        try {
            $message = $update?->getMessage();
            $callback = $update?->getCallbackQuery();
            $chatId = $message?->getChat()?->getId();
            if (!$chatId) {
                $chatId = $callback?->getMessage()?->getChat()?->getId();
            }
            if (!$chatId) {
                return;
            }

            $from = $callback
                ? $callback->getFrom()
                : $message?->getFrom();
            TelegramUser::query()->updateOrCreate(
                ['chat_id' => (int) $chatId],
                [
                    'first_name' => $from?->getFirstName(),
                    'last_name' => $from?->getLastName(),
                    'username' => $from?->getUsername(),
                ]
            );
        } catch (\Throwable $e) {
            Log::warning('telegram.webhook: upsert user failed', ['message' => $e->getMessage()]);
        }
    }

    private function upsertUserPhone(int $chatId, string $phone): void
    {
        $phone = trim($phone);
        if ($phone === '') {
            return;
        }

        try {
            TelegramUser::query()->updateOrCreate(
                ['chat_id' => $chatId],
                ['phone' => $phone]
            );
        } catch (\Throwable $e) {
            Log::warning('telegram.webhook: upsert phone failed', ['message' => $e->getMessage()]);
        }
    }

    private function isSubscribed(int $userId, int $chatIdForLog = 0): bool
    {
        $targets = TelegramRequiredChat::query()
            ->where('enabled', true)
            ->orderBy('id')
            ->get(['chat']);

        if ($targets->isEmpty()) {
            return true;
        }

        foreach ($targets as $row) {
            $target = (string) $row->chat;
            try {
                $member = Telegram::getChatMember([
                    'chat_id' => $target,
                    'user_id' => $userId,
                ]);
                $status = (string) ($member['status'] ?? ($member->getStatus() ?? ''));
                Log::info('telegram.webhook: subscription status', [
                    'chat_id' => $chatIdForLog > 0 ? $chatIdForLog : $userId,
                    'user_id' => $userId,
                    'target' => $target,
                    'status' => $status,
                ]);
                if (in_array($status, ['left', 'kicked'], true)) {
                    return false;
                }
            } catch (\Throwable $e) {
                Log::warning('telegram.webhook: subscription check failed', [
                    'chat_id' => $chatIdForLog > 0 ? $chatIdForLog : $userId,
                    'user_id' => $userId,
                    'target' => $target,
                    'message' => $e->getMessage(),
                ]);
                return false;
            }
        }

        return true;
    }

    private function requiredChatsForPrompt(): string
    {
        $targets = TelegramRequiredChat::query()
            ->where('enabled', true)
            ->orderBy('id')
            ->get(['chat', 'title', 'invite_url']);

        if ($targets->isEmpty()) {
            return '';
        }

        $lines = [];
        foreach ($targets as $row) {
            $label = trim((string) ($row->title ?: $row->chat));
            $url = trim((string) $row->invite_url);
            if ($url !== '') {
                $lines[] = $label . "\n" . $url;
            } else {
                $lines[] = $label;
            }
        }
        return implode("\n\n", $lines);
    }

    private function mustRequireSubscription(int $chatId): bool
    {
        $limit = (int) (TelegramSetting::query()
            ->where('key', 'subscribe_after_downloads')
            ->value('value') ?? env('TELEGRAM_SUBSCRIBE_AFTER_DOWNLOADS', 5));
        if ($limit <= 0) {
            return false;
        }

        $user = TelegramUser::query()->where('chat_id', $chatId)->first();
        if (!$user) {
            return false;
        }

        $downloads = (int) ($user->downloads_count ?? 0);
        $must = $downloads >= $limit;
        Log::info('telegram.webhook: mustRequireSubscription', [
            'chat_id' => $chatId,
            'downloads' => $downloads,
            'limit' => $limit,
            'must' => $must,
        ]);
        return $must;
    }

    private function incrementDownloads(int $chatId): void
    {
        TelegramUser::query()->updateOrCreate(
            ['chat_id' => $chatId],
            []
        );
        TelegramUser::query()->where('chat_id', $chatId)->increment('downloads_count');

        $downloads = (int) (TelegramUser::query()->where('chat_id', $chatId)->value('downloads_count') ?? 0);
        Log::info('telegram.webhook: incrementDownloads', [
            'chat_id' => $chatId,
            'downloads' => $downloads,
        ]);
    }

    private function maybeSendAd(int $chatId): void
    {
        try {
            $minutes = (int) env('TELEGRAM_AD_THROTTLE_MINUTES', 60);
            if ($minutes < 0) {
                $minutes = 60;
            }
            if ($minutes === 0) {
                return;
            }

            $key = 'tg:ad:last:' . $chatId;
            $ttl = now()->addMinutes($minutes);
            $allowed = Cache::add($key, 1, $ttl);
            if (!$allowed) {
                Log::info('telegram.webhook: ad throttled', [
                    'chat_id' => $chatId,
                    'minutes' => $minutes,
                ]);
                return;
            }

            $ad = TelegramAd::query()->where('enabled', true)->inRandomOrder()->first();
            if (!$ad) {
                Log::info('telegram.webhook: no enabled ads', [
                    'chat_id' => $chatId,
                ]);
                return;
            }

            $text = (string) $ad->text;
            $imagePath = trim((string) ($ad->image_path ?? ''));

            if ($imagePath !== '') {
                $absolute = storage_path('app/public/' . $imagePath);
                if (is_file($absolute)) {
                    Telegram::sendPhoto([
                        'chat_id' => $chatId,
                        'photo' => InputFile::create($absolute, basename($absolute)),
                        'caption' => $text,
                    ]);
                    return;
                }
            }

            Telegram::sendMessage([
                'chat_id' => $chatId,
                'text' => $text,
            ]);
        } catch (\Throwable $e) {
            Log::warning('telegram.webhook: send ad failed', ['message' => $e->getMessage()]);
        }
    }
}
