<?php
/**
 * Mirai Theme - Helper Functions Module
 * 辅助函数模块
 * 
 * 包含：HTML转义、时间格式化、设备检测、头像处理等通用工具函数
 */

if (!defined('__TYPECHO_ROOT_DIR__')) exit;

function Mirai_getDefaultAvatar() {
    return Mirai_getThemeUrl() . '/assets/images/default.png';
}

function Mirai_getUserAvatar($userId = null) {

    if ($userId === null) {
        $user = Mirai_user();
        if ($user->hasLogin()) {
            $userId = $user->uid;
        } else {
            return Mirai_getDefaultAvatar();
        }
    }

    if (!is_numeric($userId)) {
        return Mirai_getDefaultAvatar();
    }

    $db = \Typecho\Db::get();
    $user = $db->fetchRow($db->select('avatar')->from('table.users')->where('uid = ?', (int)$userId));
    
    if (!empty($user['avatar'])) {
        return Mirai_normalizeUrl($user['avatar']);
    }
    
    return Mirai_getDefaultAvatar();
}

function Mirai_uploadAvatar($file, $userId) {

    if ($file['error'] !== UPLOAD_ERR_OK) {
        return ['success' => false, 'message' => '上传失败，请重试'];
    }

    $allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];

    $imageInfo = @getimagesize($file['tmp_name']);
    if ($imageInfo === false) {
        return ['success' => false, 'message' => '无法识别图片格式'];
    }
    
    $mimeType = $imageInfo['mime'];
    $imageType = $imageInfo[2];
    
    if (!in_array($mimeType, $allowedTypes)) {
        return ['success' => false, 'message' => '仅支持 JPG、PNG、GIF、WebP 格式的图片'];
    }

    $maxSize = 2 * 1024 * 1024;
    if ($file['size'] > $maxSize) {
        return ['success' => false, 'message' => '图片大小不能超过 2MB'];
    }

    $uploadDir = __TYPECHO_ROOT_DIR__ . '/usr/uploads/avatars/';
    if (!is_dir($uploadDir)) {
        mkdir($uploadDir, 0755, true);
    }

    $extension = image_type_to_extension($imageType) ?: '.jpg';
    $filename = 'avatar_' . $userId . '_' . time() . $extension;
    $filepath = $uploadDir . $filename;

    if (!move_uploaded_file($file['tmp_name'], $filepath)) {
        return ['success' => false, 'message' => '文件保存失败'];
    }

    $options = Mirai_opt();
    $url = \Typecho_Common::url('usr/uploads/avatars/' . $filename, $options->siteUrl);
    
    return ['success' => true, 'url' => $url, 'message' => '上传成功'];
}

function Mirai_deleteOldAvatar($avatarUrl) {
    if (empty($avatarUrl)) return;
    
    $options = Mirai_opt();
    $siteUrl = $options->siteUrl;

    if (strpos($avatarUrl, $siteUrl) !== false) {

        $relativePath = str_replace($siteUrl, '', $avatarUrl);
        $relativePath = ltrim($relativePath, '/');

        if (strpos($relativePath, '..') !== false) {
            return; // 禁止路径遍历
        }

        if (strpos($relativePath, 'usr/uploads/avatars/') !== 0) {
            return;
        }
        
        $path = __TYPECHO_ROOT_DIR__ . '/' . $relativePath;
        $path = preg_replace('#/+#', '/', $path);
        
        if (file_exists($path) && is_file($path)) {
            @unlink($path);
        }
    }
}

function Mirai_formatISODate($timestamp) {
    if (class_exists('\Typecho\Date')) {
        return (new \Typecho\Date($timestamp))->format('c');
    } elseif (class_exists('Typecho_Date')) {
        return (new Typecho_Date($timestamp))->format('c');
    }

    $options = Mirai_opt();
    $timezone = $options->timezone; 
    $localTime = $timestamp + $timezone;
    $datePart = gmdate('Y-m-d\TH:i:s', $localTime);
    $offsetHours = floor($timezone / 3600);
    $offsetMinutes = floor(($timezone % 3600) / 60);
    $offsetPart = sprintf('%+03d:%02d', $offsetHours, $offsetMinutes);
    return $datePart . $offsetPart;
}

function Mirai_isMobile() {
    $ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
    if (preg_match('/Android|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i', $ua)) {
        return true;
    }
    if (preg_match('/iPad/i', $ua)) {
        return true;
    }
    if (strpos($ua, 'Macintosh') !== false && isset($_SERVER['HTTP_SEC_CH_UA_MOBILE']) && $_SERVER['HTTP_SEC_CH_UA_MOBILE'] === '?1') {
        return true;
    }
    return false;
}

function Mirai_formatTime($timestamp) {
    $diff = time() - $timestamp;
    
    if ($diff < 60) {
        return '刚刚';
    } elseif ($diff < 3600) {
        return floor($diff / 60) . '分钟前';
    } elseif ($diff < 86400) {
        return floor($diff / 3600) . '小时前';
    } elseif ($diff < 604800) {
        return floor($diff / 86400) . '天前';
    } else {
        if (class_exists('\Typecho\Date')) {
            return (new \Typecho\Date($timestamp))->format('Y-m-d');
        } elseif (class_exists('Typecho_Date')) {
            return (new Typecho_Date($timestamp))->format('Y-m-d');
        }
        $options = Mirai_opt();
        return gmdate('Y-m-d', $timestamp + $options->timezone);
    }
}

function Mirai_formatNumber($num, $type = 'auto') {
    $num = (int)$num;
    
    if ($type === 'views') {
        if ($num >= 10000) return round($num / 10000, 1) . 'W';
        if ($num >= 1000) return round($num / 1000, 1) . 'K';
        return (string)$num;
    }
    
    if ($num >= 100000000) {
        return round($num / 100000000, 1) . '亿';
    } elseif ($num >= 10000) {
        return round($num / 10000, 1) . '万';
    } elseif ($num >= 1000) {
        return round($num / 1000, 1) . 'K';
    }
    return (string)$num;
}

function Mirai_renderLogo($options) {
    $siteTitle = $options->siteTitle ?: $options->title;
    
    $logoImage = !empty($options->logoImage) ? $options->logoImage : 'usr/themes/Mirai/assets/images/logo.png';
    $darkLogoImage = !empty($options->darkLogoImage) ? $options->darkLogoImage : '';
    $logoAlt = !empty($options->logoAlt) ? $options->logoAlt : $siteTitle;
    $logoHeight = !empty($options->logoHeight) ? intval($options->logoHeight) : 40;
    
    $logoUrl = Mirai_normalizeUrl($logoImage);
    $heightAttr = ' height="' . $logoHeight . '"';
    
    if ($darkLogoImage) {
        $darkLogoUrl = Mirai_normalizeUrl($darkLogoImage);
        echo '<img class="mirai-logo-light" src="' . htmlspecialchars($logoUrl) . '" alt="' . htmlspecialchars($logoAlt) . '"' . $heightAttr . '>';
        echo '<img class="mirai-logo-dark" src="' . htmlspecialchars($darkLogoUrl) . '" alt="' . htmlspecialchars($logoAlt) . '"' . $heightAttr . '>';
    } else {
        echo '<img src="' . htmlspecialchars($logoUrl) . '" alt="' . htmlspecialchars($logoAlt) . '"' . $heightAttr . '>';
    }
}

function Mirai_isSkeletonEnabled($source = null) {
    $options = $source && !empty($source->options) ? $source->options : Mirai_opt();
    $archiveAjax = isset($options->archivePaginationMode) && $options->archivePaginationMode === 'ajax';
    $commentAjax = isset($options->commentsPaginationMode) && $options->commentsPaginationMode === 'ajax';
    if ($archiveAjax || $commentAjax) {
        return true;
    }
    if ($source && method_exists($source, 'is') && $source->is('index')) {
        return true;
    }
    return false;
}

function Mirai_renderSkeletonArticleCards($count = 6, $gridColumns = 3, $wrapContainer = true) {
    $count = max(1, min($count, 50));
    if ($wrapContainer) {
        $html = '<section class="article-list" data-grid="' . $gridColumns . '">';
    } else {
        $html = '';
    }

    for ($i = 0; $i < $count; $i++) {
        $html .= '<article class="gt-article-item">';
        $html .= '<span class="mirai-skeleton-cover"></span>';
        $html .= '<header>';
        $html .= '<div class="title"><a><span class="mirai-skeleton-line" style="width:96%"></span></a></div>';
        $html .= '<div class="gt-article-taglist"><span class="mirai-skeleton-line" style="width:60%"></span></div>';
        $html .= '</header></article>';
    }

    return $html . ($wrapContainer ? '</section>' : '');
}

function Mirai_renderSkeletonSingleColumnArticles($count = 4, $wrapContainer = true) {
    $count = max(1, min($count, 50));
    if ($wrapContainer) {
        $html = '<section class="article-list single-column" data-grid="1">';
    } else {
        $html = '';
    }

    for ($i = 0; $i < $count; $i++) {
        $html .= '<article class="gt-article-item">';
        $html .= '<section class="gt-article-banner">';
        $html .= '<span class="mirai-skeleton-cover"></span>';
        $html .= '</section>';
        $html .= '<div class="gt-article-content">';
        $html .= '<header>';
        $html .= '<div class="title"><a><span class="mirai-skeleton-line" style="width:88%"></span></a></div>';
        $html .= '</header>';
        $html .= '<div class="gt-article-excerpt"><span class="mirai-skeleton-line" style="width:100%"></span></div>';
        $html .= '<div class="gt-article-taglist"><span class="mirai-skeleton-line" style="width:60%"></span></div>';
        $html .= '</div></article>';
    }

    return $html . ($wrapContainer ? '</section>' : '');
}

function Mirai_renderSkeletonCommentThread($count = 3) {
    $count = max(1, min($count, 50));
    $html = '<div class="gt-comment-list">';

    for ($i = 0; $i < $count; $i++) {
        $html .= '<div class="gt-comment-item">';
        $html .= '<div class="gt-comment-avatar"><span class="mirai-skeleton-circle"></span></div>';
        $html .= '<div class="gt-comment-main">';
        $html .= '<div class="gt-comment-head"><span class="mirai-skeleton-line" style="width:30%"></span></div>';
        $html .= '<div class="gt-comment-content"><span class="mirai-skeleton-line" style="width:100%"></span></div>';
        $html .= '</div></div>';
    }

    return $html . '</div>';
}

function Mirai_renderHomeTabSkeletonTemplate($source = null) {
    $options = Mirai_opt();
    $gridLayoutValue = !empty($options->gridLayout) ? $options->gridLayout : '3';
    $gridLayout = Mirai_getFeatureValue($gridLayoutValue, '3', 'grid_layout');
    $pageSize = intval($options->pageSize ?: 5);
    $pageSize = max(1, min(50, $pageSize));

    if ($gridLayout === '1') {
        return Mirai_renderSkeletonSingleColumnArticles($pageSize, false);
    }

    return Mirai_renderSkeletonArticleCards($pageSize, $gridLayoutValue, false);
}

function Mirai_buildAjaxPaginationHtml($params) {
    $scopeType = isset($params['scopeType']) ? $params['scopeType'] : 'archive';
    $currentPage = isset($params['current']) ? (int)$params['current'] : 1;
    $totalPages = isset($params['total']) ? (int)$params['total'] : 1;
    $nextUrl = isset($params['nextUrl']) ? $params['nextUrl'] : '';
    $triggerMode = isset($params['triggerMode']) && $params['triggerMode'] === 'auto' ? 'auto' : 'button';
    $direction = isset($params['direction']) ? $params['direction'] : 'forward';
    $ariaLabel = isset($params['ariaLabel']) ? $params['ariaLabel'] : ($scopeType === 'comments' ? '评论分页' : '分页');
    $hasMore = isset($params['hasMore']) ? (bool)$params['hasMore'] : false;

    if (!$hasMore) {
        return '<nav class="mirai-ajax-pagination is-complete" data-mirai-ajax-scope="' . htmlspecialchars($scopeType, ENT_QUOTES, 'UTF-8') . '" data-state="complete" aria-label="' . htmlspecialchars($ariaLabel, ENT_QUOTES, 'UTF-8') . '"><span class="mirai-ajax-complete-text">— 已加载全部 —</span></nav>';
    }

    if ($nextUrl === '') return '';

    $isBackward = ($direction === 'backward');
    $buttonText = $triggerMode === 'auto'
        ? ($isBackward ? '上一页' : '下一页')
        : '加载更多';
    $hiddenAttr = $triggerMode === 'auto' ? ' hidden aria-hidden="true" tabindex="-1"' : '';

    $html = '<nav class="mirai-ajax-pagination mirai-ajax-trigger-' . $triggerMode . '" data-mirai-ajax-scope="' . htmlspecialchars($scopeType, ENT_QUOTES, 'UTF-8') . '" data-mirai-ajax-trigger="' . $triggerMode . '" data-mirai-original-trigger="' . $triggerMode . '" data-mirai-ajax-current="' . $currentPage . '" data-mirai-ajax-total="' . $totalPages . '" data-mirai-ajax-direction="' . $direction . '" aria-label="' . htmlspecialchars($ariaLabel, ENT_QUOTES, 'UTF-8') . '">';
    $html .= '<div class="mirai-ajax-loader" aria-hidden="true">' . Mirai_renderAjaxPaginationLoader($scopeType) . '</div>';
    $html .= '<a href="' . htmlspecialchars($nextUrl, ENT_QUOTES, 'UTF-8') . '" class="mirai-ajax-next' . ($triggerMode === 'button' ? ' mirai-ajax-next-button' : '') . '"' . $hiddenAttr . '>' . htmlspecialchars($buttonText, ENT_QUOTES, 'UTF-8') . '</a>';
    if ($triggerMode === 'auto') {
        $html .= '<div class="mirai-ajax-sentinel" aria-hidden="true"></div>';
    }
    $html .= '</nav>';

    return $html;
}

