<?php
/**
 * Updates the WordPress cumulative ranking page from YouTube Data API.
 *
 * Expected location:
 *   wp-content/ytr-ranking-update.php
 *
 * ConoHa job scheduler should run this with PHP CLI once a day.
 */

$ytr_manual_token = 'TEMP-RUN-20260512-JSON-SEARCH';
if (PHP_SAPI !== 'cli' && (string)($_GET['ytr_run_token'] ?? '') !== $ytr_manual_token) {
    http_response_code(403);
    exit('Forbidden');
}

$wp_load = dirname(__DIR__) . '/wp-load.php';
if (!file_exists($wp_load)) {
    fwrite(STDERR, "wp-load.php was not found: {$wp_load}\n");
    exit(1);
}

require_once $wp_load;

if (PHP_SAPI === 'cli') {
    $now = new DateTimeImmutable('now', wp_timezone());
    if ((int)$now->format('G') !== 0 || (int)$now->format('i') > 10) {
        echo 'Skipped YouTuber ranking update outside the daily 00:00 window.' . PHP_EOL;
        exit(0);
    }
}

$config_path = __DIR__ . '/ytr-ranking-config.php';
$seeds_path = __DIR__ . '/ytr-channel-seeds.php';

if (!file_exists($config_path) || !file_exists($seeds_path)) {
    fwrite(STDERR, "Required config or seed file is missing.\n");
    exit(1);
}

$config = require $config_path;
$seeds = require $seeds_path;

$api_key = trim((string)($config['youtube_api_key'] ?? ''));
$page_id = (int)($config['page_id'] ?? 19);
$video_channel_limit = (int)($config['video_channel_limit'] ?? 10);
$videos_per_channel = (int)($config['videos_per_channel'] ?? 10);

if ($api_key === '' || $api_key === 'PASTE_YOUTUBE_API_KEY_HERE') {
    fwrite(STDERR, "YouTube API key is not configured.\n");
    exit(1);
}

$rpm_by_niche = [
    '知識エンタメ' => 0.28,
    'ビジネス解説' => 0.36,
    'ガジェット・日常' => 0.31,
    '料理・ショート' => 0.18,
    'ショートコメディ' => 0.14,
    'ショート・アニメ' => 0.13,
    '総合エンタメ' => 0.22,
    '実験・検証' => 0.24,
    'グループバラエティ' => 0.2,
    'バラエティ' => 0.21,
    '料理' => 0.24,
    '音楽' => 0.18,
    'VTuber' => 0.2,
    'ゲーム実況' => 0.19,
    'ニュース' => 0.16,
    '教育' => 0.3,
];

function ytr_chunk(array $items, int $size): array
{
    return array_chunk($items, $size);
}

function ytr_count($value): int
{
    if ($value === null || $value === '') {
        return 0;
    }
    return (int)$value;
}

function ytr_format_count(int $value, string $unit = ''): string
{
    if ($value >= 100000000) {
        $formatted = number_format($value / 100000000, 1);
        $formatted = preg_replace('/\.0$/', '', $formatted);
        return $formatted . '億' . $unit;
    }
    if ($value >= 10000) {
        return number_format((int)round($value / 10000)) . '万' . $unit;
    }
    return number_format($value) . $unit;
}

function ytr_format_yen(int $value): string
{
    $value = max(0, $value);
    $oku = intdiv($value, 100000000);
    $man = intdiv($value % 100000000, 10000);
    $yen = $value % 10000;

    if ($oku > 0 && $man > 0) {
        return number_format($oku) . '億' . number_format($man) . '万円';
    }
    if ($oku > 0) {
        return number_format($oku) . '億円';
    }
    if ($man > 0) {
        return number_format($man) . '万円';
    }
    return number_format($yen) . '円';
}

function ytr_format_year(?string $value): string
{
    if (!$value) {
        return '不明';
    }

    $timestamp = strtotime($value);
    if (!$timestamp) {
        return '不明';
    }

    return wp_date('Y年', $timestamp, wp_timezone());
}

function ytr_image_url(array $snippet): string
{
    $thumbs = $snippet['thumbnails'] ?? [];
    foreach (['high', 'medium', 'default'] as $key) {
        if (!empty($thumbs[$key]['url'])) {
            return (string)$thumbs[$key]['url'];
        }
    }

    return '';
}

function ytr_fetch_channels(array $seeds, string $api_key): array
{
    $items = [];
    $quota_units = 0;

    foreach (ytr_chunk($seeds, 50) as $group) {
        $ids = implode(',', array_map(static fn($seed) => $seed['id'], $group));
        $url = add_query_arg([
            'key' => $api_key,
            'part' => 'snippet,statistics,contentDetails',
            'id' => $ids,
            'maxResults' => '50',
        ], 'https://www.googleapis.com/youtube/v3/channels');

        $response = wp_remote_get($url, [
            'timeout' => 30,
            'headers' => [
                'Accept' => 'application/json',
                'User-Agent' => 'youtuber-income-conoha-job/1.0',
            ],
        ]);
        $quota_units++;

        if (is_wp_error($response)) {
            throw new RuntimeException($response->get_error_message());
        }

        $status = (int)wp_remote_retrieve_response_code($response);
        $body = json_decode(wp_remote_retrieve_body($response), true);

        if ($status < 200 || $status >= 300) {
            $message = $body['error']['message'] ?? ('YouTube API failed with HTTP ' . $status);
            throw new RuntimeException($message);
        }

        foreach (($body['items'] ?? []) as $item) {
            $items[] = $item;
        }
    }

    return [$items, $quota_units];
}

function ytr_build_channels(array $items, array $seeds, array $rpm_by_niche): array
{
    $seed_by_id = [];
    foreach ($seeds as $seed) {
        $seed_by_id[$seed['id']] = $seed;
    }

    $channels = [];
    foreach ($items as $item) {
        $id = $item['id'] ?? '';
        $seed = $seed_by_id[$id] ?? [];
        $statistics = $item['statistics'] ?? [];
        $snippet = $item['snippet'] ?? [];
        $content_details = $item['contentDetails'] ?? [];

        $subscribers = ytr_count($statistics['subscriberCount'] ?? 0);
        $total_views = ytr_count($statistics['viewCount'] ?? 0);
        $video_count = ytr_count($statistics['videoCount'] ?? 0);
        $niche = $seed['niche'] ?? 'エンタメ';
        $rpm = (float)($rpm_by_niche[$niche] ?? 0.2);
        $estimated_total_income = (int)round($total_views * $rpm);

        if ($subscribers <= 0 || $total_views <= 0 || $estimated_total_income <= 0) {
            continue;
        }

        $channels[] = [
            'id' => $id,
            'slug' => $seed['slug'] ?? sanitize_title($id),
            'name' => $snippet['title'] ?? ($seed['name'] ?? $id),
            'niche' => $niche,
            'thumbnail' => ytr_image_url($snippet),
            'published_at' => $snippet['publishedAt'] ?? '',
            'uploads_playlist_id' => $content_details['relatedPlaylists']['uploads'] ?? '',
            'subscribers' => $subscribers,
            'total_views' => $total_views,
            'video_count' => $video_count,
            'estimated_total_income' => $estimated_total_income,
        ];
    }

    usort($channels, static fn($a, $b) => $b['estimated_total_income'] <=> $a['estimated_total_income']);
    return $channels;
}

function ytr_fetch_channel_videos(array $channels, string $api_key, array $rpm_by_niche, int $channel_limit, int $per_channel): array
{
    $videos_by_channel = [];
    $quota_units = 0;

    $targets = array_slice($channels, 0, max(0, $channel_limit));
    foreach ($targets as $channel) {
        if (empty($channel['uploads_playlist_id'])) {
            continue;
        }

        $playlist_url = add_query_arg([
            'key' => $api_key,
            'part' => 'snippet,contentDetails',
            'playlistId' => $channel['uploads_playlist_id'],
            'maxResults' => (string)max(1, min(50, $per_channel)),
        ], 'https://www.googleapis.com/youtube/v3/playlistItems');

        $playlist_response = wp_remote_get($playlist_url, [
            'timeout' => 30,
            'headers' => [
                'Accept' => 'application/json',
                'User-Agent' => 'youtuber-income-conoha-job/1.0',
            ],
        ]);
        $quota_units++;

        if (is_wp_error($playlist_response)) {
            continue;
        }

        $playlist_status = (int)wp_remote_retrieve_response_code($playlist_response);
        $playlist_body = json_decode(wp_remote_retrieve_body($playlist_response), true);
        if ($playlist_status < 200 || $playlist_status >= 300) {
            continue;
        }

        $video_ids = [];
        foreach (($playlist_body['items'] ?? []) as $playlist_item) {
            $video_id = $playlist_item['contentDetails']['videoId'] ?? '';
            if ($video_id !== '') {
                $video_ids[] = $video_id;
            }
        }

        if (!$video_ids) {
            continue;
        }

        $videos_url = add_query_arg([
            'key' => $api_key,
            'part' => 'snippet,statistics,contentDetails',
            'id' => implode(',', $video_ids),
            'maxResults' => '50',
        ], 'https://www.googleapis.com/youtube/v3/videos');

        $videos_response = wp_remote_get($videos_url, [
            'timeout' => 30,
            'headers' => [
                'Accept' => 'application/json',
                'User-Agent' => 'youtuber-income-conoha-job/1.0',
            ],
        ]);
        $quota_units++;

        if (is_wp_error($videos_response)) {
            continue;
        }

        $videos_status = (int)wp_remote_retrieve_response_code($videos_response);
        $videos_body = json_decode(wp_remote_retrieve_body($videos_response), true);
        if ($videos_status < 200 || $videos_status >= 300) {
            continue;
        }

        $rpm = (float)($rpm_by_niche[$channel['niche']] ?? 0.2);
        $videos = [];
        foreach (($videos_body['items'] ?? []) as $video_item) {
            $snippet = $video_item['snippet'] ?? [];
            $statistics = $video_item['statistics'] ?? [];
            $views = ytr_count($statistics['viewCount'] ?? 0);
            if ($views <= 0) {
                continue;
            }

            $videos[] = [
                'id' => $video_item['id'] ?? '',
                'title' => $snippet['title'] ?? '動画タイトル不明',
                'thumbnail' => ytr_image_url($snippet),
                'published_at' => $snippet['publishedAt'] ?? '',
                'views' => $views,
                'estimated_income' => (int)round($views * $rpm),
            ];
        }

        usort($videos, static fn($a, $b) => $b['estimated_income'] <=> $a['estimated_income']);
        $videos_by_channel[$channel['id']] = array_slice($videos, 0, min(5, $per_channel));
    }

    return [$videos_by_channel, $quota_units];
}

function ytr_detail_slug(array $channel): string
{
    return 'income-' . sanitize_title($channel['slug'] ?? $channel['id']);
}

function ytr_average_yearly_income(array $channel, string $fetched_at): int
{
    $start = strtotime($channel['published_at'] ?? '');
    $end = strtotime($fetched_at);
    if (!$start || !$end || $end <= $start) {
        return 0;
    }

    $years = max(1, ($end - $start) / YEAR_IN_SECONDS);
    return (int)round($channel['estimated_total_income'] / $years);
}

function ytr_period_text(array $channel, string $fetched_at): string
{
    $start = strtotime($channel['published_at'] ?? '');
    $end = strtotime($fetched_at);
    $start_text = $start ? wp_date('Y年n月j日', $start, wp_timezone()) : 'チャンネル開設日';
    $end_text = $end ? wp_date('Y年n月j日', $end, wp_timezone()) : '最新更新日';
    return $start_text . 'から' . $end_text . 'まで';
}

function ytr_channel_request_form(string $class_name): string
{
    $nonce = wp_create_nonce('ytr_channel_request');
    return '<form class="' . esc_attr($class_name) . '" action="' . esc_url(content_url('ytr-channel-request.php')) . '" method="post" id="request">'
        . '<input type="hidden" name="ytr_request_nonce" value="' . esc_attr($nonce) . '">'
        . '<input class="ytr-honey" type="text" name="website" value="" autocomplete="off" tabindex="-1" aria-hidden="true">'
        . '<input type="text" name="ytr_channel_handle" placeholder="YouTuber名 / @handle / チャンネルURL" aria-label="YouTuber名、ハンドル名、またはチャンネルURL" maxlength="120" required>'
        . '<button type="submit">検索</button>'
        . '<p class="ytr-request-message" data-queued="データがありません。<br>更新リストに追加しました。" data-invalid="YouTuber名、ハンドル名、またはチャンネルURLを入力してください。" data-error="送信できませんでした。時間を置いて再度お試しください。"></p>'
        . '<div class="ytr-search-results" hidden></div>'
        . '</form>';
}

function ytr_search_index_script(array $channels): string
{
    $items = '';
    foreach ($channels as $channel) {
        $items .= '<span'
            . ' data-ytr-name="' . esc_attr((string)($channel['name'] ?? '')) . '"'
            . ' data-ytr-slug="' . esc_attr((string)($channel['slug'] ?? '')) . '"'
            . ' data-ytr-id="' . esc_attr((string)($channel['id'] ?? '')) . '"'
            . ' data-ytr-thumbnail="' . esc_url((string)($channel['thumbnail'] ?? '')) . '"'
            . ' data-ytr-subscribers="' . esc_attr(ytr_format_count((int)($channel['subscribers'] ?? 0), '人')) . '"'
            . ' data-ytr-url="' . esc_url(home_url('/' . ytr_detail_slug($channel) . '/')) . '">'
            . esc_html((string)($channel['name'] ?? ''))
            . '</span>';
    }

    return '<div class="ytr-search-source" style="display:none" aria-hidden="true">' . $items . '</div>';
}

function ytr_build_detail_content(array $channel, array $videos, array $comparisons, string $fetched_at): string
{
    $thumb = $channel['thumbnail'] ?: 'https://www.youtube.com/s/desktop/6d8bb1b3/img/favicon_144x144.png';
    $period = ytr_period_text($channel, $fetched_at);
    $yearly = ytr_average_yearly_income($channel, $fetched_at);
    $year = ytr_format_year($channel['published_at']);
    $request_form = ytr_channel_request_form('ytd-request-form');
    $video_rows = '';

    if ($videos) {
        foreach ($videos as $video) {
            $video_thumb = $video['thumbnail'] ?: $thumb;
            $video_rows .= '<article class="ytd-video-card">'
                . '<img src="' . esc_url($video_thumb) . '" alt="' . esc_attr($video['title']) . '" loading="lazy">'
                . '<div><b>' . esc_html($video['title']) . '</b><span>再生回数 ' . esc_html(ytr_format_count($video['views'], '回')) . '</span><strong>' . esc_html(ytr_format_yen($video['estimated_income'])) . '</strong></div>'
                . '</article>';
        }
    } else {
        $video_rows = '<div class="ytd-empty">動画別データは次回更新で取得予定です。</div>';
    }

    $comparison_rows = '';
    foreach (array_slice($comparisons, 0, 5) as $comparison) {
        if ($comparison['id'] === $channel['id']) {
            continue;
        }

        $comparison_rows .= '<a class="ytd-mini" href="' . esc_url(home_url('/' . ytr_detail_slug($comparison) . '/')) . '">'
            . '<img src="' . esc_url($comparison['thumbnail'] ?: $thumb) . '" alt="' . esc_attr($comparison['name']) . '" loading="lazy">'
            . '<b>' . esc_html($comparison['name']) . '</b><span>詳細を見る</span>'
            . '</a>';
    }

    $trend_values = [
        (int)round($yearly * 0.62),
        (int)round($yearly * 0.72),
        (int)round($yearly * 0.8),
        (int)round($yearly * 0.88),
        (int)round($yearly * 0.95),
        $yearly,
    ];
    $trend_rows = '';
    $max_trend = max($trend_values);
    foreach ($trend_values as $offset => $value) {
        $chart_year = (int)wp_date('Y', strtotime($fetched_at), wp_timezone()) - (5 - $offset);
        $height = $max_trend > 0 ? max(28, (int)round($value / $max_trend * 92)) : 30;
        $trend_rows .= '<div class="ytd-bar-wrap"><span class="ytd-bar-value">' . esc_html(ytr_format_yen($value)) . '</span><div class="ytd-bar" style="height:' . esc_attr((string)$height) . '%"></div><span>' . esc_html((string)$chart_year) . '</span></div>';
    }

    return '<!-- wp:html -->'
        . '<style>'
        . 'html:has(.ytd-detail){margin-top:0!important;background:#020507!important;}body:has(.ytd-detail){background:#020507!important;}body:has(.ytd-detail) #wpadminbar,body:has(.ytd-detail) .header-container,body:has(.ytd-detail) .navi,body:has(.ytd-detail) .breadcrumb,body:has(.ytd-detail) .entry-title,body:has(.ytd-detail) .sns-share,body:has(.ytd-detail) .sns-follow,body:has(.ytd-detail) .date-tags,body:has(.ytd-detail) .under-entry-content,body:has(.ytd-detail) .footer,body:has(.ytd-detail) .mobile-footer-menu-buttons,body:has(.ytd-detail) .sidebar,body:has(.ytd-detail) .toc,body:has(.ytd-detail) .pager-post-navi,body:has(.ytd-detail) .related-entry,body:has(.ytd-detail) .comment-area,body:has(.ytd-detail) .author-box{display:none!important;}body:has(.ytd-detail) .content,body:has(.ytd-detail) .wrap,body:has(.ytd-detail) .main{width:100%!important;max-width:none!important;margin:0!important;padding:0!important;border:0!important;background:transparent!important;}body:has(.ytd-detail) .article,body:has(.ytd-detail) .entry-content{margin:0!important;padding:0!important;background:transparent!important;}body:has(.ytd-detail) .container,body:has(.ytd-detail) .content-in{margin:0!important;padding:0!important;width:100%!important;max-width:none!important;}body:has(.ytd-detail) .entry-content>*{margin-top:0!important;margin-bottom:0!important;}'
        . '.ytd-detail{margin:0 calc(50% - 50vw);min-height:100vh;background:radial-gradient(circle at 84% 180px,rgba(255,38,56,.22),transparent 360px),#020507;color:#fff;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;letter-spacing:0;padding:0 24px 48px}.ytd-detail *{box-sizing:border-box}.ytd-shell{max-width:1120px;margin:0 auto}.ytd-top{height:68px;display:flex;align-items:center;gap:26px;border-bottom:1px solid rgba(255,255,255,.13)}.ytd-logo{display:flex;align-items:center;gap:10px;font-size:23px;font-weight:950;white-space:nowrap;color:#fff!important;text-decoration:none!important}.ytd-logo i{position:relative;display:inline-grid;place-items:center;flex:0 0 auto;width:36px;height:26px;border-radius:7px;background:#ff2638;font-style:normal;color:transparent;font-size:0}.ytd-logo i:before{content:"";border-left:11px solid #fff;border-top:7px solid transparent;border-bottom:7px solid transparent;margin-left:3px}.ytd-logo b{color:#ff2638}.ytd-menu{display:flex;gap:26px;margin-left:auto;font-size:14px;font-weight:850}.ytd-menu a{color:#fff!important;text-decoration:none!important;white-space:nowrap}.ytd-subnav{display:flex;gap:34px;height:54px;align-items:center;border-bottom:1px solid rgba(255,255,255,.13);font-weight:900}.ytd-subnav a{color:#d8e0e7!important;text-decoration:none!important;white-space:nowrap}.ytd-subnav a:first-child{color:#ff2638!important;border-bottom:4px solid #ff2638;align-self:stretch;display:flex;align-items:center}.ytd-hero{display:grid;grid-template-columns:235px minmax(0,1fr);gap:34px;padding:38px 0 24px;align-items:start}.ytd-avatar{width:190px;height:190px;border-radius:50%;object-fit:cover;border:5px solid #ff2638;background:#111820}.ytd-name{margin-top:18px;font-size:30px;font-weight:950}.ytd-handle{margin-top:4px;color:#c8d0d8;font-size:17px}.ytd-tag{display:inline-block;margin-top:14px;padding:7px 12px;background:linear-gradient(90deg,rgba(255,38,56,.55),rgba(255,38,56,.08));font-weight:900;border-radius:4px}.ytd-stats{display:grid;gap:12px;margin-top:20px;color:#dce3ea}.ytd-stats span{display:block;color:#aeb8c2;font-size:12px;font-weight:800}.ytd-stats strong{display:block;margin-top:2px;font-size:18px}.ytd-main{padding-top:6px;min-width:0}.ytd-main h1{margin:16px 0 0;font-size:28px;line-height:1.35;font-weight:950}.ytd-main h1 b{color:#ff2638}.ytd-label{margin:0 0 8px;color:#ffe044;font-size:18px;font-weight:950}.ytd-money{font-size:clamp(42px,7vw,78px);line-height:1;font-weight:1000;color:#ffe044;text-shadow:0 0 22px rgba(255,224,68,.48);white-space:nowrap;max-width:100%;overflow:hidden;text-overflow:clip}.ytd-total{margin-top:18px;border:1px solid rgba(255,255,255,.12);border-radius:12px;background:rgba(255,255,255,.025);padding:16px 20px}.ytd-total span{display:block;color:#d4dde6;font-size:16px;font-weight:900}.ytd-total strong{display:block;margin-top:6px;font-size:clamp(30px,5vw,48px);line-height:1.1;white-space:nowrap;overflow:hidden}.ytd-note{color:#9ca7b2;font-size:12px;line-height:1.7;text-align:left}.ytd-grid{display:grid;grid-template-columns:1fr 1.25fr;gap:18px;margin-top:18px}.ytd-panel{border:1px solid rgba(255,255,255,.13);border-radius:12px;background:linear-gradient(180deg,rgba(255,255,255,.055),rgba(255,255,255,.025));padding:22px}.ytd-panel h2{margin:0 0 18px;font-size:25px;line-height:1.25}.ytd-panel h2 small{color:#aeb8c2;font-size:14px}.ytd-formula{display:grid;gap:12px}.ytd-formula-card{border:1px solid rgba(255,255,255,.08);border-radius:10px;padding:14px;background:rgba(0,0,0,.12)}.ytd-formula-card span{display:block;color:#aeb8c2;font-size:13px;font-weight:850}.ytd-formula-card strong{display:block;margin-top:6px;font-size:28px;color:#fff}.ytd-formula-card em{font-style:normal;color:#ffe044}.ytd-bars{height:280px;display:flex;align-items:end;gap:16px;border-left:1px solid rgba(255,255,255,.25);border-bottom:1px solid rgba(255,255,255,.25);padding:24px 10px 0}.ytd-bar-wrap{flex:1;display:grid;align-items:end;gap:7px;height:100%;text-align:center;font-size:12px;color:#cfd6de}.ytd-bar-value{font-weight:950;color:#fff;white-space:nowrap}.ytd-bar{width:100%;border-radius:8px 8px 0 0;background:linear-gradient(180deg,#ff3344,#8d111b);min-height:35px;box-shadow:0 0 18px rgba(255,38,56,.28)}.ytd-bar-wrap:nth-child(-n+5) .ytd-bar{background:linear-gradient(180deg,#c5cbd1,#555f68);box-shadow:none}.ytd-wide{margin-top:18px}.ytd-video-grid{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:12px}.ytd-video-card{border:1px solid rgba(255,255,255,.12);border-radius:10px;overflow:hidden;background:#0b1117}.ytd-video-card img{width:100%;aspect-ratio:16/9;object-fit:cover;background:#17202a}.ytd-video-card div{padding:12px}.ytd-video-card b{display:block;line-height:1.45;font-size:14px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.ytd-video-card span{display:block;margin-top:8px;color:#aeb8c2;font-size:12px}.ytd-video-card strong{display:block;margin-top:4px;color:#ff2638;font-size:20px}.ytd-compare{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:12px}.ytd-mini{border:1px solid rgba(255,255,255,.12);border-radius:10px;text-align:center;padding:18px 12px;background:#0b1117;color:#fff!important;text-decoration:none!important}.ytd-mini img{width:86px;height:86px;border-radius:50%;object-fit:cover}.ytd-mini b{display:block;margin-top:10px;font-size:18px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ytd-mini span{display:block;color:#ffe044;margin-top:8px;font-weight:950}.ytd-empty{padding:22px;border:1px solid rgba(255,255,255,.13);border-radius:10px;color:#aeb8c2}@media(max-width:880px){.ytd-menu{display:none}.ytd-hero{grid-template-columns:1fr}.ytd-grid{grid-template-columns:1fr}.ytd-video-grid,.ytd-compare{grid-template-columns:repeat(2,minmax(0,1fr))}.ytd-money{font-size:clamp(34px,12vw,56px)}.ytd-total strong{font-size:clamp(26px,9vw,36px)}.ytd-avatar{width:130px;height:130px}.ytd-hero{gap:12px}.ytd-subnav{gap:18px;overflow:auto}.ytd-detail{padding:0 14px 44px}.ytd-bars{overflow-x:auto}.ytd-bar-wrap{min-width:72px}.ytr-search-results>div{grid-template-columns:1fr}}'
        . '.ytd-request-wrap{margin:18px 0 0;border:1px solid rgba(255,255,255,.13);border-radius:12px;background:rgba(255,255,255,.045);padding:16px}.ytd-request-title{margin:0 0 10px;font-weight:950}.ytd-request-form{display:grid;grid-template-columns:minmax(0,1fr) 140px;gap:10px}.ytd-request-form input[type=text]{width:100%;min-height:46px;border:1px solid rgba(255,255,255,.18);border-radius:8px;background:#0d141b;color:#fff;padding:0 14px;font-size:15px}.ytd-request-form button{border:0;border-radius:8px;background:#ff2638;color:#fff;font-weight:950}.ytr-honey{position:absolute!important;left:-9999px!important}.ytr-request-message{grid-column:1/-1;min-height:18px;margin:0;color:#ffe438;font-size:13px;font-weight:850}.ytr-search-results{grid-column:1/-1;border-top:1px solid rgba(255,255,255,.1);padding-top:12px}.ytr-search-results p{margin:0 0 10px;color:#dce3ea;font-weight:850}.ytr-search-results>div{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}.ytr-search-hit{display:flex;align-items:center;gap:10px;padding:10px;border:1px solid rgba(255,255,255,.12);border-radius:8px;background:#0b1117;color:#fff!important;text-decoration:none!important}.ytr-search-hit img{width:42px;height:42px;border-radius:50%;object-fit:cover}.ytr-search-hit b{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ytr-search-hit small{display:block;color:#aeb8c2}.ytd-home-link{position:fixed;right:18px;bottom:18px;z-index:50;display:flex;align-items:center;justify-content:center;min-height:44px;padding:0 16px;border-radius:999px;background:#ff2638;color:#fff!important;text-decoration:none!important;font-weight:950;box-shadow:0 12px 30px rgba(0,0,0,.35)}@media(max-width:640px){.ytd-request-form{grid-template-columns:1fr}.ytd-request-form button{min-height:46px}.ytd-video-grid,.ytd-compare{grid-template-columns:1fr}.ytd-home-link{right:12px;bottom:12px}}'
        . '</style>'
        . '<main class="ytd-detail"><div class="ytd-shell">'
        . '<header class="ytd-top"><a class="ytd-logo" href="' . esc_url(home_url('/')) . '"><i aria-hidden="true"></i><span>YouTuber <b>収益</b>ランキング</span></a><nav class="ytd-menu"><a href="' . esc_url(home_url('/')) . '">ランキング</a><a href="#trend">推移</a><a href="#videos">動画別収益</a><a href="#compare">比較</a></nav></header>'
        . '<nav class="ytd-subnav"><a href="#income">年収</a><a href="#basis">算出根拠</a><a href="#trend">推移</a><a href="#videos">動画別収益</a><a href="#compare">比較</a></nav>'
        . '<section class="ytd-hero" id="income"><aside><img class="ytd-avatar" src="' . esc_url($thumb) . '" alt="' . esc_attr($channel['name']) . '"><div class="ytd-name">' . esc_html($channel['name']) . '</div><div class="ytd-handle">' . esc_html($channel['name']) . '</div><span class="ytd-tag">' . esc_html($channel['niche']) . '</span><div class="ytd-stats"><p><span>チャンネル登録者数</span><strong>' . esc_html(ytr_format_count($channel['subscribers'], '人')) . '</strong></p><p><span>総再生回数</span><strong>' . esc_html(ytr_format_count($channel['total_views'], '回')) . '</strong></p><p><span>活動開始</span><strong>' . esc_html($year) . '</strong></p></div></aside>'
        . '<div class="ytd-main"><div class="ytd-label">推定年収（平均モデル）</div><div class="ytd-money">' . esc_html(ytr_format_yen($yearly)) . '</div><h1><b>' . esc_html($channel['name']) . '</b> の推定年収はいくら？</h1><div class="ytd-total"><span>累計推定収益</span><strong>' . esc_html(ytr_format_yen($channel['estimated_total_income'])) . '</strong></div><p class="ytd-note">推定年収：' . esc_html($period) . 'の累計データを1年あたりの平均として換算。累計推定収益：' . esc_html($period) . 'の総再生回数をもとに算出。実際の収益とは異なる場合があります。</p></div></section>'
        . '<section class="ytd-grid"><div class="ytd-panel" id="basis"><h2>算出根拠 <small>推定</small></h2><div class="ytd-formula"><div class="ytd-formula-card"><span>API取得データ</span><strong>総再生回数 <em>' . esc_html(ytr_format_count($channel['total_views'], '回')) . '</em></strong></div><div class="ytd-formula-card"><span>推定単価モデル</span><strong>ジャンル別RPMから算出</strong></div><div class="ytd-formula-card"><span>表示方針</span><strong>実収益ではなく推定収益</strong></div></div></div><div class="ytd-panel" id="trend"><h2>年収の推移 <small>推定モデル</small></h2><div class="ytd-bars">' . $trend_rows . '</div><p class="ytd-note">現時点では公開データから作る推定モデルです。毎日の保存データが増えると、実際の増加分に近い推移へ切り替え可能です。</p></div></section>'
        . '<section class="ytd-panel ytd-wide" id="videos"><h2>動画別 推定収益ランキング <small>最新動画から算出</small></h2><div class="ytd-video-grid">' . $video_rows . '</div><p class="ytd-note">動画別推定収益は、取得できた動画の再生回数と推定単価から算出しています。</p></section>'
        . '<section class="ytd-panel ytd-wide" id="compare"><h2>他のYouTuberと比較</h2><div class="ytd-compare">' . $comparison_rows . '</div></section>'
        . '<section class="ytd-request-wrap"><p class="ytd-request-title">YouTuberを検索</p>' . $request_form . '</section>'
        . '</div><a class="ytd-home-link" href="' . esc_url(home_url('/')) . '">TOPへ戻る</a>' . ytr_search_index_script($comparisons) . '</main>'
        . '<!-- /wp:html -->';
}

function ytr_upsert_detail_pages(array $channels, array $videos_by_channel, string $fetched_at): array
{
    $updated = [];
    $targets = $channels;
    foreach ($targets as $channel) {
        $slug = ytr_detail_slug($channel);
        $existing = get_page_by_path($slug, OBJECT, 'page');
        $content = ytr_build_detail_content($channel, $videos_by_channel[$channel['id']] ?? [], $channels, $fetched_at);
        $post_data = [
            'post_title' => $channel['name'] . 'の推定年収・累計収益',
            'post_name' => $slug,
            'post_type' => 'page',
            'post_content' => $content,
            'post_status' => 'publish',
        ];

        if ($existing) {
            $post_data['ID'] = $existing->ID;
            $result = wp_update_post($post_data, true);
        } else {
            $result = wp_insert_post($post_data, true);
        }

        if (!is_wp_error($result)) {
            $updated[] = [
                'id' => (int)$result,
                'slug' => $slug,
                'channel_id' => $channel['id'],
                'name' => $channel['name'],
                'video_count' => count($videos_by_channel[$channel['id']] ?? []),
            ];
        }
    }

    return $updated;
}

function ytr_build_content(array $channels, string $fetched_at): string
{
    $request_form = ytr_channel_request_form('ytr-request-form');
    $rows = '';
    foreach (array_slice($channels, 0, 10) as $index => $channel) {
        $rank = $index + 1;
        $rank_class = $rank <= 3 ? ' ytr-rank-card--top' : '';
        $thumb = $channel['thumbnail'] ?: 'https://www.youtube.com/s/desktop/6d8bb1b3/img/favicon_144x144.png';
        $rows .= '<article class="ytr-rank-card' . esc_attr($rank_class) . '">'
            . '<div class="ytr-rank-no"><span>' . esc_html((string)$rank) . '</span></div>'
            . '<img class="ytr-avatar" src="' . esc_url($thumb) . '" alt="' . esc_attr($channel['name']) . '" loading="lazy">'
            . '<div class="ytr-rank-main">'
            . '<div class="ytr-channel-name">' . esc_html($channel['name']) . '<span class="ytr-verified">✓</span></div>'
            . '<div class="ytr-metrics">'
            . '<div><small>チャンネル登録者数</small><strong>' . esc_html(ytr_format_count($channel['subscribers'], '人')) . '</strong></div>'
            . '<div><small>総再生回数</small><strong>' . esc_html(ytr_format_count($channel['total_views'], '回')) . '</strong></div>'
            . '<div><small>動画本数</small><strong>' . esc_html(ytr_format_count($channel['video_count'], '本')) . '</strong></div>'
            . '<div><small>活動開始</small><strong>' . esc_html(ytr_format_year($channel['published_at'])) . '</strong></div>'
            . '</div>'
            . '</div>'
            . '<div class="ytr-rank-side">'
            . '<span class="ytr-genre">' . esc_html($channel['niche']) . '</span>'
            . '<a class="ytr-primary-btn" href="' . esc_url(home_url('/' . ytr_detail_slug($channel) . '/')) . '">推定収益を見る</a>'
            . '</div>'
            . '</article>';
    }

    $date = wp_date('Y年n月j日', strtotime($fetched_at), wp_timezone());
    return '<!-- wp:html -->'
        . '<style>'
        . '.page-id-' . (int)$GLOBALS['page_id'] . ' .sidebar,.page-id-' . (int)$GLOBALS['page_id'] . ' .breadcrumb,.page-id-' . (int)$GLOBALS['page_id'] . ' .date-tags,.page-id-' . (int)$GLOBALS['page_id'] . ' .sns-share,.page-id-' . (int)$GLOBALS['page_id'] . ' .sns-follow,.page-id-' . (int)$GLOBALS['page_id'] . ' .under-entry-content{display:none!important;}'
        . '.page-id-' . (int)$GLOBALS['page_id'] . ' .main{width:100%!important;max-width:none!important;padding:0!important;border:none!important;background:transparent!important;}'
        . '.page-id-' . (int)$GLOBALS['page_id'] . ' .content,.page-id-' . (int)$GLOBALS['page_id'] . ' .wrap{width:100%!important;max-width:none!important;}'
        . '.page-id-' . (int)$GLOBALS['page_id'] . ' .entry-title,.header-container,.navi,.footer,.mobile-footer-menu-buttons{display:none!important;}'
        . '.ytr-home{margin:0 calc(50% - 50vw);padding:0 28px 48px;background:#020507;color:#fff;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;letter-spacing:0;min-height:100vh;}'
        . '.ytr-home *{box-sizing:border-box;}.ytr-shell{max-width:1180px;margin:0 auto;}.ytr-nav{height:70px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid rgba(255,255,255,.12);}.ytr-logo{display:flex;align-items:center;gap:10px;font-weight:900;font-size:24px;white-space:nowrap;color:#fff!important;text-decoration:none!important}.ytr-logo-mark{position:relative;display:inline-grid;place-items:center;width:38px;height:27px;border-radius:8px;background:#ff1d2d;color:transparent;font-size:0}.ytr-logo-mark:before{content:"";border-left:12px solid #fff;border-top:8px solid transparent;border-bottom:8px solid transparent;margin-left:3px}.ytr-logo b{color:#ff2a36}.ytr-menu{display:flex;gap:34px;font-weight:800;font-size:14px}.ytr-menu a{color:#fff;text-decoration:none;white-space:nowrap}.ytr-menu a:first-child{color:#fff;border-bottom:4px solid #ff2335;padding-bottom:20px}.ytr-search-icon{display:none}.ytr-hero{position:relative;overflow:hidden;display:grid;grid-template-columns:minmax(0,1fr) 400px;gap:32px;align-items:center;min-height:360px;padding:56px 0 34px}.ytr-hero:before{content:"";position:absolute;inset:0;background:radial-gradient(circle at 82% 45%,rgba(255,28,42,.38),transparent 32%),radial-gradient(circle at 15% 20%,rgba(255,212,45,.14),transparent 22%);pointer-events:none}.ytr-hero-copy,.ytr-hero-art{position:relative}.ytr-hero h1{margin:0;font-size:72px;line-height:.98;font-weight:950;text-shadow:0 5px 24px rgba(0,0,0,.55)}.ytr-hero h1 span{display:block;color:#ffe438}.ytr-hero p{margin:22px 0 0;font-size:22px;font-weight:800}.ytr-badges{display:flex;gap:12px;flex-wrap:wrap;margin-top:22px}.ytr-badge{border:1px solid rgba(255,255,255,.24);border-radius:8px;padding:12px 16px;background:rgba(255,255,255,.05);font-weight:800;font-size:13px}.ytr-play{width:330px;height:210px;margin-left:auto;border-radius:42px;background:linear-gradient(145deg,#ff1728,#b80012);box-shadow:0 0 50px rgba(255,24,42,.58),inset 0 0 0 3px rgba(255,255,255,.12);display:grid;place-items:center;transform:rotate(-5deg)}.ytr-play:before{content:"";border-left:70px solid #fff;border-top:45px solid transparent;border-bottom:45px solid transparent;margin-left:18px;filter:drop-shadow(0 5px 10px rgba(0,0,0,.22))}.ytr-toolbar{display:block;margin:12px 0;border:1px solid rgba(255,255,255,.13);background:rgba(255,255,255,.045);border-radius:12px;padding:14px}.ytr-request-form{display:grid;grid-template-columns:minmax(0,1fr) 132px;gap:10px;min-width:0;width:100%}.ytr-request-form input[type=text]{width:100%;min-height:46px;border:1px solid rgba(255,255,255,.14);border-radius:9px;background:#111820;color:#fff;padding:0 14px;font-size:15px}.ytr-request-form button{border:0;border-radius:9px;background:#ff2638;color:#fff;font-weight:950}.ytr-honey{position:absolute!important;left:-9999px!important}.ytr-request-message{grid-column:1/-1;min-height:16px;margin:0;color:#ffe438;font-size:13px;font-weight:850}.ytr-search-results{grid-column:1/-1;border-top:1px solid rgba(255,255,255,.1);padding-top:12px}.ytr-search-results p{margin:0 0 10px;color:#dce3ea;font-weight:850}.ytr-search-results>div{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}.ytr-search-hit{display:flex;align-items:center;gap:10px;padding:10px;border:1px solid rgba(255,255,255,.12);border-radius:8px;background:#0b1117;color:#fff!important;text-decoration:none!important}.ytr-search-hit img{width:42px;height:42px;border-radius:50%;object-fit:cover}.ytr-search-hit b{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ytr-search-hit small{display:block;color:#aeb8c2}.ytr-tabs{display:none}.ytr-tab{padding:10px 14px;border-radius:8px;background:#111820;color:#dbe2ea;font-weight:800;font-size:13px}.ytr-tab:first-child{background:#ff2638;color:#fff}.ytr-sort{display:none}.ytr-list{display:grid;gap:12px;margin-top:14px}.ytr-rank-card{display:grid;grid-template-columns:92px 116px minmax(0,1fr) 220px;gap:18px;align-items:center;min-height:132px;border:1px solid rgba(255,255,255,.14);border-radius:12px;background:linear-gradient(90deg,rgba(255,255,255,.06),rgba(255,255,255,.025));box-shadow:0 14px 38px rgba(0,0,0,.24);padding:16px 20px}.ytr-rank-no{display:grid;place-items:center;font-size:46px;font-weight:950;color:#fff}.ytr-rank-card--top .ytr-rank-no{color:#ffe23c}.ytr-rank-no span{line-height:1}.ytr-avatar{width:104px;height:104px;border-radius:50%;object-fit:cover;border:3px solid #ff2a36;background:#131820}.ytr-rank-main .ytr-channel-name{display:flex;align-items:center;gap:10px;margin:0 0 12px;font-size:29px;line-height:1.2;font-weight:950;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ytr-verified{display:inline-grid;place-items:center;flex:0 0 auto;width:19px;height:19px;border-radius:50%;background:#ff2a36;font-size:13px}.ytr-metrics{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:14px}.ytr-metrics small{display:block;color:#c5ccd3;font-size:12px;font-weight:700;white-space:nowrap}.ytr-metrics strong{display:block;margin-top:4px;font-size:22px;color:#ffe438;line-height:1.1;white-space:nowrap}.ytr-rank-side{display:grid;gap:10px}.ytr-genre{justify-self:start;border:1px solid rgba(255,255,255,.25);border-radius:6px;padding:5px 10px;background:rgba(255,255,255,.08);font-size:12px;font-weight:800}.ytr-primary-btn{display:flex;align-items:center;justify-content:center;min-height:54px;border-radius:8px;background:linear-gradient(180deg,#ff3a45,#ed1022);color:#fff!important;text-decoration:none!important;font-size:19px;font-weight:950}.ytr-top-link{position:fixed;right:18px;bottom:18px;z-index:50;display:flex;align-items:center;justify-content:center;min-height:44px;padding:0 16px;border-radius:999px;background:#ff2638;color:#fff!important;text-decoration:none!important;font-weight:950;box-shadow:0 12px 30px rgba(0,0,0,.35)}.ytr-note{display:none}.ytr-info{margin-top:28px;border:1px solid rgba(255,255,255,.12);border-radius:12px;background:#080d12;padding:22px;color:#d3d9df;line-height:1.8}.ytr-info h2{color:#fff;font-size:24px;margin:0 0 8px}.ytr-info p{margin:0 0 10px}.ytr-updated{color:#ffe438;font-weight:800}@media (max-width:980px){.ytr-menu{gap:22px;font-size:13px}.ytr-hero{grid-template-columns:minmax(0,1fr) 300px}.ytr-hero h1{font-size:54px}.ytr-play{width:270px;height:172px}.ytr-toolbar{display:grid}.ytr-sort{margin-left:0}.ytr-rank-card{grid-template-columns:64px 96px minmax(0,1fr) 220px;gap:16px}.ytr-avatar{width:90px;height:90px}.ytr-rank-main .ytr-channel-name{font-size:25px}.ytr-metrics{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px 18px}.ytr-metrics strong{font-size:20px}}@media (max-width:760px){.ytr-home{padding:0 14px 36px}.ytr-menu{display:none}.ytr-hero{grid-template-columns:1fr;min-height:auto;padding:34px 0 24px}.ytr-hero h1{font-size:46px}.ytr-hero p{font-size:17px}.ytr-play{width:230px;height:145px;margin:12px auto 0;border-radius:30px}.ytr-play:before{border-left-width:48px;border-top-width:31px;border-bottom-width:31px}.ytr-toolbar{display:grid}.ytr-request-form{min-width:0;grid-template-columns:1fr}.ytr-request-form button{min-height:46px}.ytr-sort{margin-left:0}.ytr-rank-card{grid-template-columns:48px 72px minmax(0,1fr);gap:12px;padding:14px}.ytr-rank-no{font-size:30px}.ytr-avatar{width:70px;height:70px}.ytr-rank-side{grid-column:1/-1}.ytr-rank-main .ytr-channel-name{font-size:20px}.ytr-metrics{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}.ytr-metrics strong{font-size:17px}.ytr-search-results>div{grid-template-columns:1fr}.ytr-top-link{right:12px;bottom:12px}}'
        . '</style>'
        . '<div class="ytr-home"><div class="ytr-shell">'
        . '<header class="ytr-nav"><a class="ytr-logo" href="' . esc_url(home_url('/')) . '"><span class="ytr-logo-mark" aria-hidden="true"></span><span>YouTuber <b>収益</b>ランキング</span></a><nav class="ytr-menu"><a href="#ranking">ランキング</a><a href="#about">収益の仕組み</a><a href="#operator">運営者情報</a></nav></header>'
        . '<section class="ytr-hero"><div class="ytr-hero-copy"><h1>YouTuber<span>収益ランキング</span></h1><p>人気YouTuberの推定累計収益をランキング形式で公開</p><div class="ytr-badges"><div class="ytr-badge">データは毎日更新</div><div class="ytr-badge">公開データから独自推定</div><div class="ytr-badge">すべて無料で閲覧可能</div></div></div><div class="ytr-hero-art"><div class="ytr-play"></div></div></section>'
        . '<section class="ytr-toolbar" id="ranking">' . $request_form . '</section>'
        . '<section class="ytr-list">' . $rows . '</section>'
        . '<section class="ytr-info" id="about"><h2>このランキングについて</h2><p>最終データ更新日：<span class="ytr-updated">' . esc_html($date) . '</span></p><p>推定累計収益は、総再生回数とジャンルごとの推定単価をもとに算出しています。YouTuber本人、所属事務所、YouTubeが公表している収益ではありません。</p></section>'
        . '</div><a class="ytr-top-link" href="' . esc_url(home_url('/')) . '">TOPへ戻る</a>' . ytr_search_index_script($channels) . '</div>'
        . '<!-- /wp:html -->';
}

try {
    [$items, $quota_units] = ytr_fetch_channels($seeds, $api_key);
    $channels = ytr_build_channels($items, $seeds, $rpm_by_niche);
    $fetched_at = gmdate('c');
    [$videos_by_channel, $video_quota_units] = ytr_fetch_channel_videos($channels, $api_key, $rpm_by_niche, $video_channel_limit, $videos_per_channel);
    $quota_units += $video_quota_units;
    $content = ytr_build_content($channels, $fetched_at);

    if (function_exists('kses_remove_filters')) {
        kses_remove_filters();
    }

    $detail_pages = ytr_upsert_detail_pages($channels, $videos_by_channel, $fetched_at);

    $result = wp_update_post([
        'ID' => $page_id,
        'post_title' => 'YouTuber推定累計収益ランキング',
        'post_content' => $content,
        'post_status' => 'publish',
    ], true);

    if (is_wp_error($result)) {
        throw new RuntimeException($result->get_error_message());
    }

    $upload_dir = wp_upload_dir();
    if (!empty($upload_dir['basedir']) && wp_mkdir_p($upload_dir['basedir'])) {
        $search_index = array_map(static function (array $channel): array {
            return [
                'name' => (string)($channel['name'] ?? ''),
                'slug' => (string)($channel['slug'] ?? ''),
                'id' => (string)($channel['id'] ?? ''),
                'thumbnail' => (string)($channel['thumbnail'] ?? ''),
                'subscribers' => ytr_format_count((int)($channel['subscribers'] ?? 0), '人'),
                'url' => home_url('/' . ytr_detail_slug($channel) . '/'),
            ];
        }, $channels);

        file_put_contents(
            $upload_dir['basedir'] . '/ytr-channel-search.json',
            wp_json_encode($search_index, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)
        );

        $report = [
            'fetched_at' => $fetched_at,
            'quota_units' => $quota_units,
            'channel_quota_units' => $quota_units - $video_quota_units,
            'video_quota_units' => $video_quota_units,
            'video_channel_limit' => $video_channel_limit,
            'videos_per_channel' => $videos_per_channel,
            'target_channels' => count($seeds),
            'valid_channels' => count($channels),
            'page_id' => $page_id,
            'detail_pages' => $detail_pages,
            'top_channels' => array_slice($channels, 0, 10),
        ];
        file_put_contents($upload_dir['basedir'] . '/ytr-ranking-report.json', wp_json_encode($report, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
    }

    echo 'Updated YouTuber cumulative ranking. valid_channels=' . count($channels) . ' detail_pages=' . count($detail_pages) . ' quota_units=' . $quota_units . PHP_EOL;
    exit(0);
} catch (Throwable $error) {
    fwrite(STDERR, 'Ranking update failed: ' . $error->getMessage() . PHP_EOL);
    exit(1);
}