<?php

declare(strict_types=1);

namespace App\Service;

use App\Model\Platform;
use App\Model\Store;
use App\Model\UserDataScope;

class ScopeBitmapService
{
    /**
     * 将 store_id 数组编码为 bitmap 二进制串
     * store_id=N → bit N-1 置 1
     *
     * @param int[] $store_ids
     * @return string
     */
    public function encode(array $store_ids): string
    {
        if (empty($store_ids)) {
            return '';
        }

        $max_id = max($store_ids);
        $byte_length = intdiv($max_id - 1, 8) + 1;
        $bitmap = str_repeat("\0", $byte_length);

        foreach ($store_ids as $store_id) {
            $bit_pos = $store_id - 1;
            $byte_index = intdiv($bit_pos, 8);
            $bit_offset = $bit_pos % 8;
            $bitmap[$byte_index] = chr(ord($bitmap[$byte_index]) | (1 << $bit_offset));
        }

        return $bitmap;
    }

    /**
     * 将 bitmap 解码为 store_id 数组
     *
     * @param string $bitmap
     * @return int[]
     */
    public function decode(string $bitmap): array
    {
        $store_ids = [];
        $length = strlen($bitmap);

        for ($i = 0; $i < $length; $i++) {
            $byte = ord($bitmap[$i]);
            if ($byte === 0) {
                continue;
            }

            for ($bit = 0; $bit < 8; $bit++) {
                if (($byte >> $bit) & 1) {
                    $store_ids[] = $i * 8 + $bit + 1;
                }
            }
        }

        return $store_ids;
    }

    /**
     * 检查 bitmap 中是否包含指定 store_id
     *
     * @param string $bitmap
     * @param int $store_id
     * @return bool
     */
    public function has(string $bitmap, int $store_id): bool
    {
        $bit_pos = $store_id - 1;
        $byte_index = intdiv($bit_pos, 8);

        if ($byte_index >= strlen($bitmap)) {
            return false;
        }

        $bit_offset = $bit_pos % 8;
        return (bool) ((ord($bitmap[$byte_index]) >> $bit_offset) & 1);
    }

    /**
     * 合并多个 bitmap（OR 运算）
     *
     * @param string ...$bitmaps
     * @return string
     */
    public function merge(string ...$bitmaps): string
    {
        $bitmaps = array_filter($bitmaps, fn(string $b): bool => $b !== '');

        if (empty($bitmaps)) {
            return '';
        }

        $max_len = max(array_map('strlen', $bitmaps));
        $result = str_repeat("\0", $max_len);

        foreach ($bitmaps as $bitmap) {
            for ($i = 0; $i < strlen($bitmap); $i++) {
                $result[$i] = chr(ord($result[$i]) | ord($bitmap[$i]));
            }
        }

        return $result;
    }

    /**
     * 根据用户的 user_data_scopes 记录构建 scope bitmap
     * 处理 company/platform/store 三种 scope_type 的解析和合并
     *
     * @param int $user_id
     * @return string
     */
    public function buildForUser(int $user_id): string
    {
        $scopes = UserDataScope::query()->where('user_id', $user_id)->get();

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

        $store_ids = [];
        $grouped = $scopes->groupBy('scope_type');

        // company 类型：查该 company 下所有 store
        if ($company_scopes = $grouped->get('company')) {
            $company_ids = $company_scopes->pluck('scope_id')->toArray();
            $store_ids = array_merge(
                $store_ids,
                Store::query()->whereIn('company_id', $company_ids)->pluck('id')->toArray()
            );
        }

        // platform 类型：查该 platform 下所有 store
        if ($platform_scopes = $grouped->get('platform')) {
            $platform_ids = $platform_scopes->pluck('scope_id')->toArray();
            $store_ids = array_merge(
                $store_ids,
                Store::query()->whereIn('platform_id', $platform_ids)->pluck('id')->toArray()
            );
        }

        // store 类型：直接使用
        if ($store_scopes = $grouped->get('store')) {
            $store_ids = array_merge($store_ids, $store_scopes->pluck('scope_id')->toArray());
        }

        $store_ids = array_unique(array_map('intval', $store_ids));
        return $this->encode($store_ids);
    }

    /**
     * 根据 developer 维护的 platform 构建 scope bitmap
     *
     * @param int $user_id
     * @return string
     */
    public function buildForDeveloper(int $user_id): string
    {
        $platform_ids = Platform::query()->where('developer_id', $user_id)->pluck('id')->toArray();

        if (empty($platform_ids)) {
            return '';
        }

        $store_ids = Store::query()->whereIn('platform_id', $platform_ids)->pluck('id')->toArray();
        $store_ids = array_map('intval', $store_ids);
        return $this->encode($store_ids);
    }
}
