<?php
declare(strict_types=1);

// No top-level IO. Include only from an explicitly approved lifecycle runner.
function cdxRankRows(PDO $pdo, string $sql, array $params, int $max): array
{
    $started = microtime(true);
    $stmt = $pdo->prepare($sql);
    $stmt->execute($params);
    $rows = [];
    while (($row = $stmt->fetch(PDO::FETCH_ASSOC)) !== false) {
        $rows[] = array_change_key_case($row, CASE_LOWER);
        if (count($rows) > $max) throw new RuntimeException('rank candidate row cap exceeded');
    }
    $stmt->closeCursor();
    if (microtime(true) - $started > 5.0) {
        throw new RuntimeException('rank candidate query exceeded five seconds');
    }
    return $rows;
}

function cdxDeriveRankDepartment(PDO $ai, PDO $store, bool $dm): array
{
    if ($dm) {
        [$score, $user, $staff, $link, $department] = ['YOSHOP_USER_SCORE',
            'YOSHOP_USER', 'YDY_STAFF', 'YDY_STAFF_DEPARTMENT', 'YDY_DEPARTMENT'];
        $limitUsers = 'FETCH FIRST 50 ROWS ONLY';
        $limitLinks = 'FETCH FIRST 501 ROWS ONLY';
    } else {
        [$score, $user, $staff, $link, $department] = ['yoshop_user_score',
            'yoshop_user', 'ydy_staff', 'ydy_staff_department', 'ydy_department'];
        $limitUsers = 'LIMIT 50';
        $limitLinks = 'LIMIT 501';
    }
    $byType = [];
    $all = [];
    foreach ([1, 2, 3] as $type) {
        $sql = 'SELECT s.USER_ID,MAX(s.SCORE) BEST_SCORE FROM ' . $score . ' s'
            . ' JOIN ' . $user . ' u ON u.USER_ID=s.USER_ID AND u.IS_DELETE=0'
            . ' WHERE s.STORE_ID=10001 AND s.TYPE=? AND s.SCORE>0'
            . ' GROUP BY s.USER_ID ORDER BY BEST_SCORE DESC,s.USER_ID ASC ' . $limitUsers;
        $rows = cdxRankRows($ai, $sql, [$type], 50);
        if (!$rows) throw new RuntimeException('rank type has no positive source');
        $byType[$type] = [];
        foreach ($rows as $row) {
            $id = (int)($row['user_id'] ?? 0);
            if ($id <= 0 || (int)($row['best_score'] ?? 0) <= 0) {
                throw new RuntimeException('rank positive source key invalid');
            }
            $byType[$type][$id] = true;
            $all[$id] = true;
        }
    }
    if (count($all) > 150) throw new RuntimeException('rank AI source exceeds 150 users');
    $marks = implode(',', array_fill(0, count($all), '?'));
    $sql = 'SELECT DISTINCT sd.DEPARTMENT_UUID,s.AI_USER_ID FROM '
        . $staff . ' s JOIN ' . $link . ' sd ON sd.STAFF_UUID=s.UUID'
        . ' JOIN ' . $department . ' d ON d.UUID=sd.DEPARTMENT_UUID'
        . ' WHERE s.IS_SHOW=1 AND s.AI_USER_ID>0 AND d.STATUS=1 AND d.IS_SHOW=1'
        . ' AND s.AI_USER_ID IN (' . $marks . ')'
        . ' ORDER BY sd.DEPARTMENT_UUID,s.AI_USER_ID ' . $limitLinks;
    $links = cdxRankRows($store, $sql, array_keys($all), 501);
    if (count($links) > 500) throw new RuntimeException('rank Store links exceed 500');
    $byDepartment = [];
    foreach ($links as $row) {
        $uuid = (string)($row['department_uuid'] ?? '');
        $id = (int)($row['ai_user_id'] ?? 0);
        if ($uuid === '' || $id <= 0 || !isset($all[$id])) {
            throw new RuntimeException('rank Store relation invalid');
        }
        foreach ([1, 2, 3] as $type) {
            if (isset($byType[$type][$id])) $byDepartment[$uuid][$type][$id] = true;
        }
    }
    foreach ($byDepartment as $uuid => $types) {
        $counts = [1 => count($types[1] ?? []), 2 => count($types[2] ?? []),
            3 => count($types[3] ?? [])];
        if (min($counts) > 0) {
            return ['uuid' => $uuid, 'sha256' => hash('sha256', $uuid),
                'counts' => $counts, 'links' => count($links),
                'source_users_by_type' => [1 => count($byType[1]),
                    2 => count($byType[2]), 3 => count($byType[3])]];
        }
    }
    throw new RuntimeException('no three-type department in bounded natural sample');
}
