<?php

require_once __DIR__ . '/config.php';

function sccharacters_column_exists(PDO $db, string $table, string $column): bool
{
    $stmt = $db->query("SHOW COLUMNS FROM `{$table}` LIKE " . $db->quote($column));

    return (bool) $stmt->fetch();
}

function sccharacters_index_exists(PDO $db, string $table, string $index): bool
{
    $stmt = $db->query("SHOW INDEX FROM `{$table}` WHERE Key_name = " . $db->quote($index));

    return (bool) $stmt->fetch();
}

function sccharacters_foreign_key_exists(PDO $db, string $table, string $constraint): bool
{
    $stmt = $db->prepare(
        "SELECT COUNT(*)
         FROM information_schema.TABLE_CONSTRAINTS
         WHERE TABLE_SCHEMA = DATABASE()
           AND TABLE_NAME = :table_name
           AND CONSTRAINT_NAME = :constraint_name
           AND CONSTRAINT_TYPE = 'FOREIGN KEY'"
    );
    $stmt->execute([
        'table_name' => $table,
        'constraint_name' => $constraint,
    ]);

    return (int) $stmt->fetchColumn() > 0;
}

function sccharacters_bootstrap(): void
{
    static $bootstrapped = false;

    if ($bootstrapped) {
        return;
    }

    $db = db();

    $db->exec(
        "CREATE TABLE IF NOT EXISTS tbl_sccharacters (
            cl_sccharacter_id INT UNSIGNED NOT NULL AUTO_INCREMENT,
            cl_sccharacter_owner_auth_id INT UNSIGNED NOT NULL,
            cl_sccharacter_name VARCHAR(190) NOT NULL,
            cl_sccharacter_role VARCHAR(190) NOT NULL DEFAULT '',
            cl_sccharacter_faction VARCHAR(190) NOT NULL DEFAULT '',
            cl_sccharacter_org_rsi_url VARCHAR(255) NOT NULL DEFAULT '',
            cl_sccharacter_player_handle VARCHAR(190) NOT NULL DEFAULT '',
            cl_sccharacter_avatar_url VARCHAR(255) NOT NULL DEFAULT '',
            cl_sccharacter_description TEXT DEFAULT NULL,
            cl_sccharacter_notes TEXT DEFAULT NULL,
            cl_sccharacter_share_token VARCHAR(64) NOT NULL,
            cl_sccharacter_share_enabled TINYINT(1) NOT NULL DEFAULT 0,
            cl_sccharacter_is_pinned TINYINT(1) NOT NULL DEFAULT 0,
            cl_sccharacter_category_order TEXT DEFAULT NULL,
            cl_sccharacter_created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
            cl_sccharacter_updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
            PRIMARY KEY (cl_sccharacter_id),
            UNIQUE KEY uq_sccharacter_share_token (cl_sccharacter_share_token),
            KEY idx_sccharacter_owner (cl_sccharacter_owner_auth_id),
            KEY idx_sccharacter_name (cl_sccharacter_name),
            CONSTRAINT fk_sccharacter_owner_auth FOREIGN KEY (cl_sccharacter_owner_auth_id)
                REFERENCES tbl_auth (cl_auth_id)
                ON DELETE CASCADE
                ON UPDATE CASCADE
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
    );

    if (!sccharacters_column_exists($db, 'tbl_sccharacters', 'cl_sccharacter_owner_auth_id')) {
        $db->exec(
            'ALTER TABLE tbl_sccharacters
             ADD COLUMN cl_sccharacter_owner_auth_id INT UNSIGNED NULL AFTER cl_sccharacter_id'
        );
    }

    if (!sccharacters_column_exists($db, 'tbl_sccharacters', 'cl_sccharacter_share_token')) {
        $db->exec(
            "ALTER TABLE tbl_sccharacters
             ADD COLUMN cl_sccharacter_share_token VARCHAR(64) NOT NULL DEFAULT '' AFTER cl_sccharacter_notes"
        );
    }

    if (!sccharacters_column_exists($db, 'tbl_sccharacters', 'cl_sccharacter_share_enabled')) {
        $db->exec(
            'ALTER TABLE tbl_sccharacters
             ADD COLUMN cl_sccharacter_share_enabled TINYINT(1) NOT NULL DEFAULT 0 AFTER cl_sccharacter_share_token'
        );
    }

    if (!sccharacters_column_exists($db, 'tbl_sccharacters', 'cl_sccharacter_is_pinned')) {
        $db->exec(
            'ALTER TABLE tbl_sccharacters
             ADD COLUMN cl_sccharacter_is_pinned TINYINT(1) NOT NULL DEFAULT 0 AFTER cl_sccharacter_share_enabled'
        );
    }

    if (!sccharacters_column_exists($db, 'tbl_sccharacters', 'cl_sccharacter_org_rsi_url')) {
        $db->exec(
            "ALTER TABLE tbl_sccharacters
             ADD COLUMN cl_sccharacter_org_rsi_url VARCHAR(255) NOT NULL DEFAULT '' AFTER cl_sccharacter_faction"
        );
    }


    if (!sccharacters_column_exists($db, 'tbl_sccharacters', 'cl_sccharacter_player_handle')) {
        $db->exec(
            "ALTER TABLE tbl_sccharacters
             ADD COLUMN cl_sccharacter_player_handle VARCHAR(190) NOT NULL DEFAULT '' AFTER cl_sccharacter_org_rsi_url"
        );
    }

    if (!sccharacters_column_exists($db, 'tbl_sccharacters', 'cl_sccharacter_category_order')) {
        $db->exec(
            'ALTER TABLE tbl_sccharacters
             ADD COLUMN cl_sccharacter_category_order TEXT NULL AFTER cl_sccharacter_is_pinned'
        );
    }

    if (!sccharacters_index_exists($db, 'tbl_sccharacters', 'idx_sccharacter_owner')) {
        $db->exec(
            'ALTER TABLE tbl_sccharacters
             ADD INDEX idx_sccharacter_owner (cl_sccharacter_owner_auth_id)'
        );
    }

    if (!sccharacters_index_exists($db, 'tbl_sccharacters', 'uq_sccharacter_share_token')) {
        $db->exec(
            'ALTER TABLE tbl_sccharacters
             ADD UNIQUE KEY uq_sccharacter_share_token (cl_sccharacter_share_token)'
        );
    }

    if (!sccharacters_foreign_key_exists($db, 'tbl_sccharacters', 'fk_sccharacter_owner_auth')) {
        $db->exec(
            'ALTER TABLE tbl_sccharacters
             ADD CONSTRAINT fk_sccharacter_owner_auth FOREIGN KEY (cl_sccharacter_owner_auth_id)
                 REFERENCES tbl_auth (cl_auth_id)
                 ON DELETE CASCADE
                 ON UPDATE CASCADE'
        );
    }

    $db->exec(
        "CREATE TABLE IF NOT EXISTS tbl_sccharacteritems (
            cl_sccharacteritem_id INT UNSIGNED NOT NULL AUTO_INCREMENT,
            cl_sccharacteritem_character_id INT UNSIGNED NOT NULL,
            cl_sccharacteritem_source ENUM('base', 'custom') NOT NULL DEFAULT 'base',
            cl_sccharacteritem_scobjs_id INT UNSIGNED DEFAULT NULL,
            cl_sccharacteritem_scitemcustom_id INT(11) DEFAULT NULL,
            cl_sccharacteritem_slot VARCHAR(120) NOT NULL DEFAULT '',
            cl_sccharacteritem_quantity INT UNSIGNED DEFAULT NULL,
            cl_sccharacteritem_note TEXT DEFAULT NULL,
            cl_sccharacteritem_sort_order INT UNSIGNED NOT NULL DEFAULT 0,
            cl_sccharacteritem_created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY (cl_sccharacteritem_id),
            KEY idx_sccharacteritem_character (cl_sccharacteritem_character_id),
            KEY idx_sccharacteritem_scobjs (cl_sccharacteritem_scobjs_id),
            KEY idx_sccharacteritem_scitemcustom (cl_sccharacteritem_scitemcustom_id),
            KEY idx_sccharacteritem_character_sort (cl_sccharacteritem_character_id, cl_sccharacteritem_sort_order, cl_sccharacteritem_id),
            CONSTRAINT fk_sccharacteritem_character FOREIGN KEY (cl_sccharacteritem_character_id)
                REFERENCES tbl_sccharacters (cl_sccharacter_id)
                ON DELETE CASCADE
                ON UPDATE CASCADE,
            CONSTRAINT fk_sccharacteritem_scobjs FOREIGN KEY (cl_sccharacteritem_scobjs_id)
                REFERENCES tbl_scobjs (cl_scobjs_id)
                ON DELETE SET NULL
                ON UPDATE CASCADE,
            CONSTRAINT fk_sccharacteritem_scitemcustom FOREIGN KEY (cl_sccharacteritem_scitemcustom_id)
                REFERENCES tbl_scitemcustom (cl_scitemcustom_id)
                ON DELETE SET NULL
                ON UPDATE CASCADE
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
    );

    if (!sccharacters_column_exists($db, 'tbl_sccharacteritems', 'cl_sccharacteritem_scobjs_id')) {
        $db->exec(
            'ALTER TABLE tbl_sccharacteritems
             ADD COLUMN cl_sccharacteritem_scobjs_id INT UNSIGNED NULL AFTER cl_sccharacteritem_source'
        );
    }

    if (!sccharacters_column_exists($db, 'tbl_sccharacteritems', 'cl_sccharacteritem_scitemcustom_id')) {
        $db->exec(
            'ALTER TABLE tbl_sccharacteritems
             ADD COLUMN cl_sccharacteritem_scitemcustom_id INT(11) NULL AFTER cl_sccharacteritem_scobjs_id'
        );
    }

    if (!sccharacters_column_exists($db, 'tbl_sccharacteritems', 'cl_sccharacteritem_quantity')) {
        $db->exec(
            'ALTER TABLE tbl_sccharacteritems
             ADD COLUMN cl_sccharacteritem_quantity INT UNSIGNED NULL AFTER cl_sccharacteritem_slot'
        );
    }

    if (!sccharacters_column_exists($db, 'tbl_sccharacteritems', 'cl_sccharacteritem_sort_order')) {
        $db->exec(
            'ALTER TABLE tbl_sccharacteritems
             ADD COLUMN cl_sccharacteritem_sort_order INT UNSIGNED NOT NULL DEFAULT 0 AFTER cl_sccharacteritem_note'
        );
    }

    if (!sccharacters_index_exists($db, 'tbl_sccharacteritems', 'idx_sccharacteritem_character')) {
        $db->exec(
            'ALTER TABLE tbl_sccharacteritems
             ADD INDEX idx_sccharacteritem_character (cl_sccharacteritem_character_id)'
        );
    }

    if (!sccharacters_index_exists($db, 'tbl_sccharacteritems', 'idx_sccharacteritem_scobjs')) {
        $db->exec(
            'ALTER TABLE tbl_sccharacteritems
             ADD INDEX idx_sccharacteritem_scobjs (cl_sccharacteritem_scobjs_id)'
        );
    }

    if (!sccharacters_index_exists($db, 'tbl_sccharacteritems', 'idx_sccharacteritem_scitemcustom')) {
        $db->exec(
            'ALTER TABLE tbl_sccharacteritems
             ADD INDEX idx_sccharacteritem_scitemcustom (cl_sccharacteritem_scitemcustom_id)'
        );
    }

    if (!sccharacters_index_exists($db, 'tbl_sccharacteritems', 'idx_sccharacteritem_character_sort')) {
        $db->exec(
            'ALTER TABLE tbl_sccharacteritems
             ADD INDEX idx_sccharacteritem_character_sort (cl_sccharacteritem_character_id, cl_sccharacteritem_sort_order, cl_sccharacteritem_id)'
        );
    }

    if (!sccharacters_foreign_key_exists($db, 'tbl_sccharacteritems', 'fk_sccharacteritem_character')) {
        $db->exec(
            'ALTER TABLE tbl_sccharacteritems
             ADD CONSTRAINT fk_sccharacteritem_character FOREIGN KEY (cl_sccharacteritem_character_id)
                 REFERENCES tbl_sccharacters (cl_sccharacter_id)
                 ON DELETE CASCADE
                 ON UPDATE CASCADE'
        );
    }

    if (!sccharacters_foreign_key_exists($db, 'tbl_sccharacteritems', 'fk_sccharacteritem_scobjs')) {
        $db->exec(
            'ALTER TABLE tbl_sccharacteritems
             ADD CONSTRAINT fk_sccharacteritem_scobjs FOREIGN KEY (cl_sccharacteritem_scobjs_id)
                 REFERENCES tbl_scobjs (cl_scobjs_id)
                 ON DELETE SET NULL
                 ON UPDATE CASCADE'
        );
    }

    if (!sccharacters_foreign_key_exists($db, 'tbl_sccharacteritems', 'fk_sccharacteritem_scitemcustom')) {
        $db->exec(
            'ALTER TABLE tbl_sccharacteritems
             ADD CONSTRAINT fk_sccharacteritem_scitemcustom FOREIGN KEY (cl_sccharacteritem_scitemcustom_id)
                 REFERENCES tbl_scitemcustom (cl_scitemcustom_id)
                 ON DELETE SET NULL
                 ON UPDATE CASCADE'
        );
    }

    $stmt_missing_tokens = $db->query(
        "SELECT cl_sccharacter_id
         FROM tbl_sccharacters
         WHERE cl_sccharacter_share_token IS NULL
            OR cl_sccharacter_share_token = ''"
    );

    $stmt_update_token = $db->prepare(
        'UPDATE tbl_sccharacters
         SET cl_sccharacter_share_token = :token
         WHERE cl_sccharacter_id = :id'
    );

    foreach ($stmt_missing_tokens->fetchAll() as $row) {
        $stmt_update_token->execute([
            'token' => sccharacters_generate_share_token($db),
            'id' => (int) $row['cl_sccharacter_id'],
        ]);
    }

    $bootstrapped = true;
}

function sccharacters_generate_share_token(PDO $db): string
{
    do {
        $token = bin2hex(random_bytes(16));
        $stmt = $db->prepare(
            'SELECT cl_sccharacter_id
             FROM tbl_sccharacters
             WHERE cl_sccharacter_share_token = :token
             LIMIT 1'
        );
        $stmt->execute(['token' => $token]);
    } while ($stmt->fetch());

    return $token;
}


function sccharacters_extract_org_tag(?string $url): string
{
    $url = trim((string) $url);
    if ($url === '') {
        return '';
    }

    $path = trim((string) parse_url($url, PHP_URL_PATH));
    if ($path === '') {
        return '';
    }

    if (preg_match('~/(?:[a-z]{2}/)?orgs/([^/?#]+)~i', $path, $matches)) {
        $tag = rawurldecode((string) ($matches[1] ?? ''));
    } else {
        $segments = array_values(array_filter(explode('/', trim($path, '/')), static fn (string $segment): bool => $segment !== ''));
        $tag = $segments !== [] ? rawurldecode((string) end($segments)) : '';
    }

    $tag = preg_replace('/[^A-Za-z0-9._-]+/', '', (string) $tag);
    if (!is_string($tag)) {
        return '';
    }

    return strtoupper(trim($tag));
}

function sccharacters_resolve_org_tag(array $character): string
{
    return sccharacters_extract_org_tag((string) ($character['cl_sccharacter_org_rsi_url'] ?? ''));
}

function sccharacters_has_player_handle(array $character): bool
{
    return trim((string) ($character['cl_sccharacter_player_handle'] ?? '')) !== '';
}

function sccharacters_reindex_character_items(PDO $db, int $character_id): void
{
    if ($character_id <= 0) {
        return;
    }

    $stmt = $db->prepare(
        'SELECT cl_sccharacteritem_id
         FROM tbl_sccharacteritems
         WHERE cl_sccharacteritem_character_id = :character_id
         ORDER BY
            CASE WHEN cl_sccharacteritem_sort_order <= 0 THEN 0 ELSE 1 END,
            cl_sccharacteritem_sort_order ASC,
            cl_sccharacteritem_id ASC'
    );
    $stmt->execute(['character_id' => $character_id]);
    $item_ids = array_map('intval', $stmt->fetchAll(PDO::FETCH_COLUMN));

    if ($item_ids === []) {
        return;
    }

    $stmt_update = $db->prepare(
        'UPDATE tbl_sccharacteritems
         SET cl_sccharacteritem_sort_order = :sort_order
         WHERE cl_sccharacteritem_character_id = :character_id
           AND cl_sccharacteritem_id = :item_id'
    );

    $db->beginTransaction();

    try {
        $position = 1;
        foreach ($item_ids as $item_id) {
            $stmt_update->execute([
                'sort_order' => $position,
                'character_id' => $character_id,
                'item_id' => $item_id,
            ]);
            $position++;
        }

        $db->commit();
    } catch (Throwable $exception) {
        if ($db->inTransaction()) {
            $db->rollBack();
        }

        throw $exception;
    }
}

function sccharacters_next_item_sort_order(PDO $db, int $character_id): int
{
    if ($character_id <= 0) {
        return 1;
    }

    sccharacters_reindex_character_items($db, $character_id);

    $stmt = $db->prepare(
        'SELECT COALESCE(MAX(cl_sccharacteritem_sort_order), 0)
         FROM tbl_sccharacteritems
         WHERE cl_sccharacteritem_character_id = :character_id'
    );
    $stmt->execute(['character_id' => $character_id]);

    return ((int) $stmt->fetchColumn()) + 1;
}


function sccharacters_item_category_options(): array
{
    return [
        'weapon' => 'Armes',
        'armor' => 'Armures',
        'tools' => 'Outils',
        'consumables' => 'Consommables',
        'ammunition' => 'Munitions',
        'attachments' => 'Accessoires',
        'clothing' => 'Vêtements',
        'cargo' => 'Cargo / Conteneurs',
        'ship' => 'Composants / Véhicule',
        'access' => 'Accès / Mobilier',
        'misc' => 'Divers',
    ];
}

function sccharacters_default_category_order(): array
{
    return array_keys(sccharacters_item_category_options());
}

function sccharacters_normalize_category_order(array $category_order): array
{
    $known_categories = sccharacters_default_category_order();
    $known_lookup = array_fill_keys($known_categories, true);
    $normalized = [];

    foreach ($category_order as $category_key) {
        $category_key = trim((string) $category_key);
        if ($category_key === '' || !isset($known_lookup[$category_key]) || isset($normalized[$category_key])) {
            continue;
        }

        $normalized[$category_key] = $category_key;
    }

    foreach ($known_categories as $category_key) {
        if (!isset($normalized[$category_key])) {
            $normalized[$category_key] = $category_key;
        }
    }

    return array_values($normalized);
}

function sccharacters_parse_category_order(?string $raw_value): array
{
    $raw_value = trim((string) $raw_value);
    if ($raw_value === '') {
        return sccharacters_default_category_order();
    }

    $decoded = json_decode($raw_value, true);
    if (!is_array($decoded)) {
        return sccharacters_default_category_order();
    }

    return sccharacters_normalize_category_order($decoded);
}

function sccharacters_encode_category_order(array $category_order): string
{
    $encoded = json_encode(
        sccharacters_normalize_category_order($category_order),
        JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
    );

    return $encoded !== false
        ? $encoded
        : json_encode(sccharacters_default_category_order(), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
}

function sccharacters_character_category_order(array $character_row): array
{
    return sccharacters_parse_category_order((string) ($character_row['cl_sccharacter_category_order'] ?? ''));
}

function sccharacters_sort_items_by_category_order(array $items_by_category, array $category_order): array
{
    if ($items_by_category === []) {
        return [];
    }

    $sorted = [];
    foreach (sccharacters_normalize_category_order($category_order) as $category_key) {
        if (isset($items_by_category[$category_key])) {
            $sorted[$category_key] = $items_by_category[$category_key];
        }
    }

    foreach ($items_by_category as $category_key => $category_items) {
        if (!isset($sorted[$category_key])) {
            $sorted[$category_key] = $category_items;
        }
    }

    return $sorted;
}

function sccharacters_save_character_category_order(PDO $db, int $character_id, array $category_order): void
{
    if ($character_id <= 0) {
        return;
    }

    $stmt = $db->prepare(
        'UPDATE tbl_sccharacters
         SET cl_sccharacter_category_order = :category_order
         WHERE cl_sccharacter_id = :character_id'
    );
    $stmt->execute([
        'category_order' => sccharacters_encode_category_order($category_order),
        'character_id' => $character_id,
    ]);
}

function sccharacters_item_category_label(string $category): string
{
    $options = sccharacters_item_category_options();

    return $options[$category] ?? $options['misc'];
}

function sccharacters_item_category_slug(string $value): string
{
    $value = trim($value);
    if ($value == '') {
        return '';
    }

    if (function_exists('iconv')) {
        $converted = @iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $value);
        if ($converted !== false) {
            $value = $converted;
        }
    }

    $value = strtolower($value);
    $value = preg_replace('/[^a-z0-9]+/', '_', $value) ?? '';

    return trim($value, '_');
}

function sccharacters_string_contains_any(string $haystack, array $needles): bool
{
    foreach ($needles as $needle) {
        if ($needle !== '' && strpos($haystack, $needle) !== false) {
            return true;
        }
    }

    return false;
}

function sccharacters_normalize_item_category(?string $value): string
{
    $slug = sccharacters_item_category_slug((string) $value);
    if ($slug === '' || in_array($slug, ['auto', 'automatic', 'automatique', 'default', 'defaut'], true)) {
        return '';
    }

    $options = sccharacters_item_category_options();
    if (isset($options[$slug])) {
        return $slug;
    }

    $exact_aliases = [
        'arme' => 'weapon',
        'armes' => 'weapon',
        'weapon' => 'weapon',
        'weapons' => 'weapon',
        'armure' => 'armor',
        'armures' => 'armor',
        'armor' => 'armor',
        'armors' => 'armor',
        'outil' => 'tools',
        'outils' => 'tools',
        'tool' => 'tools',
        'tools' => 'tools',
        'consommable' => 'consumables',
        'consommables' => 'consumables',
        'usable' => 'consumables',
        'food' => 'consumables',
        'drink' => 'consumables',
        'munition' => 'ammunition',
        'munitions' => 'ammunition',
        'ammo' => 'ammunition',
        'ammunition' => 'ammunition',
        'accessoire' => 'attachments',
        'accessoires' => 'attachments',
        'attachment' => 'attachments',
        'attachments' => 'attachments',
        'vetement' => 'clothing',
        'vetements' => 'clothing',
        'clothing' => 'clothing',
        'clothes' => 'clothing',
        'cargo' => 'cargo',
        'container' => 'cargo',
        'conteneur' => 'cargo',
        'conteneurs' => 'cargo',
        'composant' => 'ship',
        'composants' => 'ship',
        'component' => 'ship',
        'components' => 'ship',
        'vaisseau' => 'ship',
        'vaisseaux' => 'ship',
        'vehicule' => 'ship',
        'vehicules' => 'ship',
        'acces' => 'access',
        'access' => 'access',
        'mobilier' => 'access',
        'divers' => 'misc',
        'misc' => 'misc',
        'autre' => 'misc',
        'autres' => 'misc',
        'other' => 'misc',
    ];

    if (isset($exact_aliases[$slug])) {
        return $exact_aliases[$slug];
    }

    $contains_aliases = [
        'weapon' => ['arme', 'weapon', 'pistol', 'rifle', 'shotgun', 'sniper', 'knife', 'blade'],
        'armor' => ['armure', 'armor', 'plating'],
        'tools' => ['outil', 'tool', 'tractor', 'mining', 'multitool', 'multi_tool', 'gadget'],
        'consumables' => ['consommable', 'consumable', 'usable', 'food', 'drink', 'med', 'medical', 'heal'],
        'ammunition' => ['munition', 'ammo', 'ammunition', 'magazine', 'grenade', 'rocket'],
        'attachments' => ['attachment', 'accessoire', 'scope', 'optic', 'silencer', 'sight', 'barrel'],
        'clothing' => ['clothing', 'vetement', 'apparel', 'outfit', 'char_clothing', 'char_head'],
        'cargo' => ['cargo', 'container', 'crate', 'box'],
        'ship' => ['component', 'composant', 'vaisseau', 'vehicule', 'thruster', 'quantum', 'powerplant', 'cooler', 'radar', 'sensor', 'shield', 'turret', 'missilelauncher', 'docking', 'flightcontroller', 'fueltank', 'fuelintake', 'aimodule'],
        'access' => ['door', 'seat', 'access', 'display', 'controlpanel', 'dashboard', 'mobilier'],
    ];

    foreach ($contains_aliases as $category => $needles) {
        if (sccharacters_string_contains_any($slug, $needles)) {
            return $category;
        }
    }

    return '';
}

function sccharacters_guess_item_category(?string $type, ?string $subtype = null): string
{
    $haystack = strtolower(trim(((string) $type) . ' ' . ((string) $subtype)));
    $slug = sccharacters_item_category_slug($haystack);

    if ($slug === '') {
        return 'misc';
    }

    if (sccharacters_string_contains_any($slug, ['char_clothing', 'char_head', 'clothing', 'apparel', 'outfit', 'hat', 'beard', 'piercing'])) {
        return 'clothing';
    }

    if (sccharacters_string_contains_any($slug, ['weaponattachment', 'attachment', 'scope', 'optic', 'sight', 'silencer', 'barrel', 'mag_mount'])) {
        return 'attachments';
    }

    if (sccharacters_string_contains_any($slug, ['missile', 'ammo', 'ammunition', 'magazine', 'rocket'])) {
        return 'ammunition';
    }

    if (sccharacters_string_contains_any($slug, ['armor', 'armure'])) {
        return 'armor';
    }

    if (sccharacters_string_contains_any($slug, ['weapon', 'gun', 'rifle', 'pistol', 'shotgun', 'sniper', 'knife', 'blade'])) {
        return 'weapon';
    }

    if (sccharacters_string_contains_any($slug, ['usable', 'consumable', 'food', 'drink', 'fps_consumable', 'med', 'medical'])) {
        return 'consumables';
    }

    if (sccharacters_string_contains_any($slug, ['tool', 'outil', 'mining', 'tractor', 'gadget', 'utility'])) {
        return 'tools';
    }

    if (sccharacters_string_contains_any($slug, ['cargo', 'container', 'crate', 'box'])) {
        return 'cargo';
    }

    if (sccharacters_string_contains_any($slug, ['door', 'seat', 'access', 'display', 'controlpanel', 'dashboard', 'shopdisplay', 'player'])) {
        return 'access';
    }

    if (sccharacters_string_contains_any($slug, ['thruster', 'quantum', 'powerplant', 'cooler', 'radar', 'sensor', 'shield', 'turret', 'flightcontroller', 'fueltank', 'fuelintake', 'a_module', 'aimodule', 'docking', 'attachedpart'])) {
        return 'ship';
    }

    return 'misc';
}

function sccharacters_resolve_item_category(?string $storedValue, ?string $type, ?string $subtype = null): string
{
    $normalized = sccharacters_normalize_item_category($storedValue);
    if ($normalized !== '') {
        return $normalized;
    }

    return sccharacters_guess_item_category($type, $subtype);
}
