<?php

namespace App\Services\Gaming;

use App\Models\IngameServiceSetting;
use App\Services\Api\API;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;

final class IngameService
{
    public function __construct(
        private API $api,
    ) {}

    public function checkServiceEnabled(string $serviceName): ?array
    {
        if (! IngameServiceSetting::isEnabled($serviceName)) {
            return [
                'success' => false,
                'error' => ucwords(str_replace('_', ' ', $serviceName)).' service is currently disabled.',
            ];
        }

        return null;
    }

    public function checkGoldBalance(int $price): ?array
    {
        $user = Auth::user();

        if ($price > 0 && $user->gold < $price) {
            return [
                'success' => false,
                'error' => "Insufficient gold. Required: {$price} gold.",
            ];
        }

        return null;
    }

    /**
     * @return array{found: bool, role: ?array, error: ?array}
     */
    public function verifyRoleOwnership(int $userId, int $roleId): array
    {
        $rolesData = $this->api->getRoles($userId);
        $roles = $rolesData['roles'] ?? [];

        foreach ($roles as $role) {
            if ((int) ($role['id'] ?? 0) === $roleId) {
                return ['found' => true, 'role' => $role, 'error' => null];
            }
        }

        return [
            'found' => false,
            'role' => null,
            'error' => [
                'success' => false,
                'error' => 'Character not found or does not belong to you.',
            ],
        ];
    }

    public function checkRoleOffline(int $roleId): ?array
    {
        $onlineList = $this->api->getOnlineList();
        $isOnline = collect($onlineList)->contains('roleid', $roleId);

        if ($isOnline) {
            return [
                'success' => false,
                'error' => 'Character must be offline. Please logout from the game first.',
            ];
        }

        return null;
    }

    public function deductGold(int $userId, int $price): void
    {
        if ($price > 0) {
            DB::table('users')
                ->where('ID', $userId)
                ->decrement('gold', $price);
        }
    }

    /**
     * @return array{success: bool, message: string, error: ?string}
     */
    public function execute(
        string $serviceName,
        int $roleId,
        callable $serviceCallback,
        bool $requireOffline = true,
        ?callable $additionalValidation = null,
    ): array {
        $error = $this->checkServiceEnabled($serviceName);
        if ($error) {
            return $error;
        }

        $user = Auth::user();
        $price = IngameServiceSetting::getPrice($serviceName);

        $error = $this->checkGoldBalance($price);
        if ($error) {
            return $error;
        }

        $roleCheck = $this->verifyRoleOwnership($user->ID, $roleId);
        if (! $roleCheck['found']) {
            return $roleCheck['error'];
        }

        if ($requireOffline) {
            $error = $this->checkRoleOffline($roleId);
            if ($error) {
                return $error;
            }
        }

        if ($additionalValidation) {
            $error = $additionalValidation($roleId, $roleCheck['role']);
            if ($error) {
                return $error;
            }
        }

        $result = $serviceCallback($roleId, $roleCheck['role']);

        if (! $result['success']) {
            return [
                'success' => false,
                'error' => ucwords(str_replace('_', ' ', $serviceName)).' failed: '.$result['message'],
            ];
        }

        $this->deductGold($user->ID, $price);

        return [
            'success' => true,
            'message' => $result['message'],
            'error' => null,
        ];
    }

    public function getItemName(int $itemId): string
    {
        $tabFilePath = public_path('img/RAE_Exported_Table.tab');

        if (! file_exists($tabFilePath)) {
            return "Item #{$itemId}";
        }

        $file = fopen($tabFilePath, 'r');
        if (! $file) {
            return "Item #{$itemId}";
        }

        while (($line = fgets($file)) !== false) {
            $fields = explode("\t", $line);

            if (isset($fields[0], $fields[2]) && trim($fields[0]) == $itemId) {
                $itemName = trim($fields[2]);
                fclose($file);

                return ! empty($itemName) ? $itemName : "Item #{$itemId}";
            }
        }

        fclose($file);

        return "Item #{$itemId}";
    }
}
