<?php
/**
 * MU Nexora — public character equipment service.
 * Read-only by design: no UPDATE/INSERT/DELETE operations exist in this class.
 */
class ProfileEquipment {

    private $character;
    private $realm;
    private $dB;
    private static $itemNameOverrides;

    public function __construct($character, $realm=null) {
        $character = trim((string)$character);
        if($character === '' || strlen($character) > 10 || preg_match('/[\x00-\x1F\x7F]/', $character)) {
            throw new InvalidArgumentException('El nombre del personaje no es válido.');
        }

        $this->realm = class_exists('NexoraRealm')
            ? NexoraRealm::validate($realm === null ? NexoraRealm::active() : $realm)
            : null;
        $this->character = $character;
        $this->dB = Connection::Database('MuOnline', $this->realm);
    }

    public function getPublicEquipment() {
        $row = $this->dB->query_fetch_single(
            "SELECT "._CLMN_CHR_NAME_." AS NexoraName, "._CLMN_CHR_CLASS_." AS NexoraClass, " .
            "DbVersion AS NexoraDbVersion, DATALENGTH("._CLMN_CHR_INV_.") AS NexoraInventoryBytes, " .
            "CONVERT(varchar(max), "._CLMN_CHR_INV_.", 2) AS NexoraInventoryHex " .
            "FROM "._TBL_CHR_." WITH (READUNCOMMITTED) WHERE "._CLMN_CHR_NAME_." = ?",
            array($this->character)
        );

        if(!is_array($row)) {
            throw new RuntimeException('No se pudo leer el equipamiento guardado de este personaje.');
        }

        $dbVersion = isset($row['NexoraDbVersion']) ? (int)$row['NexoraDbVersion'] : 0;
        $inventoryBytes = isset($row['NexoraInventoryBytes']) ? (int)$row['NexoraInventoryBytes'] : 0;
        $inventoryHex = isset($row['NexoraInventoryHex']) ? (string)$row['NexoraInventoryHex'] : '';
        $decoded = NexoraItemDecoder::decodeEquipment($inventoryHex, $dbVersion, $inventoryBytes);

        $items = array();
        foreach($decoded['items'] as $item) {
            if(!empty($item['occupied'])) {
                $item['name'] = self::resolveItemName($item['section'], $item['index'], $item['slot_label']);
                $item['image'] = self::itemImageUrl($item['section'], $item['index']);
                $item['rarity'] = self::rarity($item);
                $item['details'] = self::publicDetails($item);
            } else {
                $item['name'] = $item['slot_label'];
                $item['image'] = '';
                $item['rarity'] = 'empty';
                $item['details'] = array();
            }
            $items[] = $item;
        }

        return array(
            'ok' => true,
            'realm' => $this->realm,
            'realm_label' => class_exists('NexoraRealm') ? NexoraRealm::label($this->realm) : '',
            'character' => (string)$row['NexoraName'],
            'class' => isset($row['NexoraClass']) ? (int)$row['NexoraClass'] : 0,
            'supported' => !empty($decoded['supported']),
            'format' => $decoded['format'],
            'inventory_bytes' => $inventoryBytes,
            'warning' => isset($decoded['warning']) ? $decoded['warning'] : '',
            'source_label' => 'Último equipamiento guardado por el servidor',
            'updated_at' => date('c'),
            'items' => $items,
        );
    }

    public static function profilePortraitUrl($classCode) {
        global $custom;
        $file = 'avatar.jpg';
        if(isset($custom['character_class'][$classCode][2])) {
            $file = basename((string)$custom['character_class'][$classCode][2]);
        }

        $dir = trim((string)config('character_avatars_dir', true), '/\\');
        $jpgUrl = __PATH_TEMPLATE_IMG__ . $dir . '/' . rawurlencode($file);
        $base = pathinfo($file, PATHINFO_FILENAME);
        $webpFile = $base . '.webp';
        $webpFs = __PATH_TEMPLATE_ROOT__ . 'img/' . $dir . '/' . $webpFile;
        if(is_file($webpFs)) {
            return __PATH_TEMPLATE_IMG__ . $dir . '/' . rawurlencode($webpFile);
        }
        return $jpgUrl;
    }

    public static function profileCardBannerUrl($classCode) {
        global $custom;
        $group = isset($custom['character_class'][$classCode]['class_group'])
            ? (int)$custom['character_class'][$classCode]['class_group']
            : (int)$classCode;

        $map = array(
            0 => 'wiz.jpg',
            16 => 'knight.jpg',
            32 => 'elf.jpg',
            48 => 'mg.jpg',
            64 => 'dl.jpg',
            80 => 'sum.jpg',
            96 => 'rf.jpg',
            112 => 'gl.jpg',
            128 => 'rw.jpg',
            144 => 'sl.jpg',
        );
        if(!isset($map[$group])) return '';
        $fs = __PATH_TEMPLATE_ROOT__ . 'img/profiles/' . $map[$group];
        return is_file($fs) ? __PATH_TEMPLATE_IMG__ . 'profiles/' . rawurlencode($map[$group]) : '';
    }

    public static function itemImageUrl($section, $index) {
        $section = (int)$section;
        $index = (int)$index;
        if($section < 0 || $section > 15 || $index < 0 || $index > 511) return '';

        $relativeBase = 'img/item_images/' . $section . '/' . $index;
        foreach(array('webp', 'gif', 'png', 'jpg', 'jpeg') as $ext) {
            if(is_file(__PATH_TEMPLATE_ROOT__ . $relativeBase . '.' . $ext)) {
                return __PATH_TEMPLATE_IMG__ . 'item_images/' . $section . '/' . $index . '.' . $ext;
            }
        }
        return '';
    }

    private static function itemNameOverrides() {
        if(is_array(self::$itemNameOverrides)) return self::$itemNameOverrides;
        self::$itemNameOverrides = array();
        $file = __PATH_CONFIGS__ . 'nexora.itemnames.php';
        if(!is_file($file) || !is_readable($file)) return self::$itemNameOverrides;
        $data = include($file);
        if(is_array($data)) self::$itemNameOverrides = $data;
        return self::$itemNameOverrides;
    }

    private static function resolveItemName($section, $index, $slotLabel) {
        $key = (int)$section . ':' . (int)$index;
        $overrides = self::itemNameOverrides();
        if(isset($overrides[$key]) && is_string($overrides[$key]) && trim($overrides[$key]) !== '') {
            return trim($overrides[$key]);
        }
        // The supplied web contains item images but no reliable item-name catalog.
        // Do not invent names; expose the verified code with its equipment role.
        return $slotLabel . ' · Item ' . $key;
    }

    private static function rarity($item) {
        if(!empty($item['ancient'])) return 'ancient';
        if(!empty($item['excellent'])) return 'excellent';
        if(!empty($item['sockets_available']) || !empty($item['sockets_seeded'])) return 'socket';
        if(!empty($item['harmony'])) return 'harmony';
        return 'normal';
    }

    private static function publicDetails($item) {
        $details = array();
        if(isset($item['level'])) $details[] = 'Nivel +' . (int)$item['level'];
        if(!empty($item['skill'])) $details[] = 'Skill';
        if(!empty($item['luck'])) $details[] = 'Luck';
        if(!empty($item['additional_option_raw'])) $details[] = 'Opción adicional: nivel ' . (int)$item['additional_option_raw'];
        if(isset($item['durability'])) $details[] = 'Durabilidad: ' . (int)$item['durability'];
        if(!empty($item['excellent_count'])) $details[] = 'Excellent: ' . (int)$item['excellent_count'] . ' opción(es)';
        if(!empty($item['ancient'])) $details[] = 'Ancient detectado';
        if(!empty($item['option_380'])) $details[] = 'Opción 380';
        if(!empty($item['harmony']) && is_array($item['harmony'])) {
            $details[] = 'Harmony detectado (tipo ' . (int)$item['harmony']['type'] . ', nivel ' . (int)$item['harmony']['level'] . ')';
        }
        $socketTotal = (int)$item['sockets_available'] + (int)$item['sockets_seeded'];
        if($socketTotal > 0) {
            $details[] = 'Sockets: ' . (int)$item['sockets_seeded'] . ' ocupados / ' . (int)$item['sockets_available'] . ' disponibles';
            if(!empty($item['socket_bonus_raw'])) $details[] = 'Socket Bonus detectado';
        }
        return $details;
    }
}
