<?php

declare(strict_types=1);

namespace App\Services\Messaging;

use App\DTOs\TelegramResponse;
use DefStudio\Telegraph\Facades\Telegraph;
use DefStudio\Telegraph\Keyboard\Button;
use DefStudio\Telegraph\Keyboard\Keyboard;
use DefStudio\Telegraph\Keyboard\ReplyKeyboard;
use DefStudio\Telegraph\Keyboard\ReplyButton;
use DefStudio\Telegraph\Models\TelegraphBot as TelegraphBotModel;
use DefStudio\Telegraph\Models\TelegraphChat;
use Illuminate\Support\Facades\Log;

class TelegramBot
{
    private ?TelegraphBotModel $bot = null;
    private string $callbackPrefix = 'TBT'; // Telegram Bot Template

    public function __construct()
    {
        $this->bot = $this->getOrCreateBot();
    }

    /**
     * Get or create Telegraph bot instance
     */
    private function getOrCreateBot(): ?TelegraphBotModel
    {
        try {
            $token = config('telegraph.bots.default.token');

            if (!$token) {
                Log::error('TelegramBot: No token configured');
                return null;
            }

            // Try to find existing bot
            $bot = TelegraphBotModel::where('token', $token)->first();

            if (!$bot) {
                // Create new bot
                $bot = TelegraphBotModel::create([
                    'token' => $token,
                    'name' => config('app.name', 'Telegram Bot Template'),
                ]);
            }

            return $bot;
        } catch (\Throwable $e) {
            Log::error('TelegramBot: Failed to get or create bot', [
                'error' => $e->getMessage()
            ]);
            return null;
        }
    }

    /**
     * Get or create Telegraph chat
     */
    private function getOrCreateChat(int|string $chatId): ?TelegraphChat
    {
        $chatIdString = (string) $chatId;

        try {
            if (!$this->bot) {
                Log::error('TelegramBot: No bot available for chat creation');
                return null;
            }

            $chat = $this->bot->chats()->updateOrCreate(
                ['chat_id' => $chatIdString],
                ['name' => "Chat {$chatIdString}"]
            );

            if ($chat->chat_id !== $chatIdString) {
                $chat->chat_id = $chatIdString;
                $chat->save();
            }

            return $chat;
        } catch (\Throwable $e) {
            Log::error('TelegramBot: Failed to get or create chat', [
                'chat_id' => $chatIdString,
                'error' => $e->getMessage()
            ]);
            return null;
        }
    }

    /**
     * Ensure a string is valid UTF-8
     */
    private function ensureUtf8(string $text): string
    {
        // Quick check: if it's already valid UTF-8, return as-is
        if (preg_match('//u', $text)) {
            return $text;
        }

        // Try to detect and convert from common encodings
        $enc = function_exists('mb_detect_encoding')
            ? @mb_detect_encoding($text, ['UTF-8', 'Windows-1256', 'ISO-8859-1', 'ASCII'], true)
            : null;

        if ($enc && strtoupper($enc) !== 'UTF-8' && function_exists('mb_convert_encoding')) {
            $converted = @mb_convert_encoding($text, 'UTF-8', $enc);
            if ($converted !== false && preg_match('//u', $converted)) {
                return $converted;
            }
        }

        return $text;
    }

    /**
     * Send a text message to a user
     */
    public function sendMessage(int|string $chatId, string $text, Keyboard|ReplyKeyboard|null $keyboard = null): bool
    {
        $text = $this->ensureUtf8($text);
        $chatIdString = (string) $chatId;

        try {
            if (!$this->bot) {
                Log::error('TelegramBot: No bot available for sending message');
                return false;
            }

            $chat = $this->getOrCreateChat($chatIdString);
            if (!$chat) {
                Log::error('TelegramBot: Could not get or create chat');
                return false;
            }

            $message = $chat->message($text);

            if ($keyboard) {
                if ($keyboard instanceof ReplyKeyboard) {
                    $message = $message->replyKeyboard($keyboard);
                } else {
                    $message = $message->keyboard($keyboard);
                }
            }

            $response = $message->send();

            if (method_exists($response, 'telegraphOk') && !$response->telegraphOk()) {
                Log::error('TelegramBot: Message sending failed', [
                    'chat_id' => $chatIdString,
                    'description' => $response->json('description')
                ]);
                return false;
            }

            return true;
        } catch (\Throwable $e) {
            Log::error('TelegramBot: Message sending failed', [
                'chat_id' => $chatIdString,
                'error' => $e->getMessage()
            ]);
            return false;
        }
    }

    /**
     * Send a message with inline keyboard from array
     */
    public function sendMessageWithKeyboard(int|string $chatId, string $text, array $buttons): bool
    {
        try {
            $keyboard = $this->createKeyboardFromArray($buttons);
            return $this->sendMessage($chatId, $text, $keyboard);
        } catch (\Throwable $e) {
            Log::error('TelegramBot: Failed to send message with keyboard', [
                'chat_id' => (string) $chatId,
                'error' => $e->getMessage(),
            ]);
            return false;
        }
    }

    /**
     * Create keyboard from button array
     */
    public function createKeyboardFromArray(array $buttons): Keyboard
    {
        $keyboard = Keyboard::make();

        foreach ($buttons as $row) {
            $keyboardRow = [];
            foreach ($row as $button) {
                $text = $this->ensureUtf8($button['text'] ?? '');
                $callbackData = $button['callback_data'] ?? $button['callback'] ?? 'noop';
                
                // Ensure callback data uses our prefix
                if (!str_starts_with($callbackData, $this->callbackPrefix . ':')) {
                    $callbackData = $this->callbackPrefix . ':' . $callbackData;
                }

                $keyboardRow[] = Button::make($text)->action($callbackData);
            }
            $keyboard->row($keyboardRow);
        }

        return $keyboard;
    }

    /**
     * Answer callback query
     */
    public function answerCallbackQuery(string $callbackQueryId, string $text = null): bool
    {
        try {
            if (!$this->bot) {
                Log::error('TelegramBot: No bot available for callback query');
                return false;
            }

            $response = Telegraph::bot($this->bot)
                ->replyWebhook((int) $callbackQueryId, $text ?? 'انجام شد')
                ->send();

            if (method_exists($response, 'telegraphOk') && !$response->telegraphOk()) {
                Log::warning('TelegramBot: Callback response not ok', [
                    'callback_query_id' => $callbackQueryId,
                    'description' => $response->json('description')
                ]);
                return false;
            }

            return true;
        } catch (\Throwable $e) {
            Log::error('TelegramBot: Callback query answer failed', [
                'callback_query_id' => $callbackQueryId,
                'error' => $e->getMessage(),
            ]);
            return false;
        }
    }

    /**
     * Send typing action to show bot is processing
     */
    public function sendTyping(int|string $chatId): bool
    {
        try {
            if (!$this->bot) {
                Log::error('TelegramBot: No bot available for typing action');
                return false;
            }

            $chat = $this->getOrCreateChat((string) $chatId);
            if (!$chat) {
                return false;
            }

            $response = $chat->action('typing')->send();

            return method_exists($response, 'telegraphOk') ? $response->telegraphOk() : true;
        } catch (\Throwable $e) {
            Log::error('TelegramBot: Typing action failed', [
                'chat_id' => (string) $chatId,
                'error' => $e->getMessage()
            ]);
            return false;
        }
    }

    /**
     * Send message with typing animation
     */
    public function sendMessageWithTyping(int|string $chatId, string $text, Keyboard|ReplyKeyboard|null $keyboard = null, int $typingDuration = 2): bool
    {
        $this->sendTyping($chatId);
        if ($typingDuration > 0) {
            sleep($typingDuration);
        }
        return $this->sendMessage($chatId, $text, $keyboard);
    }

    /**
     * Send a document file to a chat
     */
    public function sendDocument(int|string $chatId, string $filePath, string $caption = null): bool
    {
        try {
            $chat = $this->getOrCreateChat((string) $chatId);
            if (!$chat) {
                return false;
            }

            $telegraph = Telegraph::chat($chat);

            if ($caption) {
                $telegraph->document($filePath)->withData('caption', $this->ensureUtf8($caption))->send();
            } else {
                $telegraph->document($filePath)->send();
            }

            return true;
        } catch (\Throwable $e) {
            Log::error('TelegramBot: Document sending failed', [
                'chat_id' => (string) $chatId,
                'file_path' => $filePath,
                'error' => $e->getMessage(),
            ]);
            return false;
        }
    }

    /**
     * Create a simple reply keyboard with persistent main menu button
     */
    public function createMainMenuReplyKeyboard(string $mainMenuText = '🏠 منوی اصلی'): ReplyKeyboard
    {
        return ReplyKeyboard::make()
            ->persistent()
            ->resize()
            ->row([
                ReplyButton::make($this->ensureUtf8($mainMenuText)),
            ]);
    }

    /**
     * Create contact request keyboard
     */
    public function createContactRequestKeyboard(string $buttonText = '📱 ارسال شماره موبایل'): ReplyKeyboard
    {
        return ReplyKeyboard::make()
            ->row([
                ReplyButton::make($this->ensureUtf8($buttonText))->requestContact(true)
            ])
            ->resize()
            ->oneTime();
    }

    /**
     * Set callback prefix for this bot instance
     */
    public function setCallbackPrefix(string $prefix): void
    {
        $this->callbackPrefix = $prefix;
    }

    /**
     * Get current callback prefix
     */
    public function getCallbackPrefix(): string
    {
        return $this->callbackPrefix;
    }

    /**
     * Send TelegramResponse object
     */
    public function sendResponse(int|string $chatId, TelegramResponse $response): bool
    {
        if (!$response->isHandled()) {
            return false;
        }

        $keyboard = null;
        if ($response->hasKeyboard()) {
            $keyboard = $this->createKeyboardFromArray($response->keyboard);
        }

        return $this->sendMessage($chatId, $response->text, $keyboard);
    }
}