<?php

namespace Core\Lib;

class BuilderPreflight
{
    private const EXTRA_TYPES = [
        'text', 'image', 'button', 'article_list', 'product_list', 'carousel',
        'image_text', 'multi_image', 'gallery', 'video_embed', 'channel_nav',
        'guestbook_form', 'qrcode', 'friend_links', 'service_panel',
        'member_portal', 'vote_form',
    ];

    public static function inspect(array $layout, $db = null, $rootPath = '')
    {
        $issues = [];
        $page = isset($layout['page']) && is_array($layout['page']) ? $layout['page'] : [];
        $extras = isset($page['frontend_extra_sections']) && is_array($page['frontend_extra_sections'])
            ? $page['frontend_extra_sections']
            : [];

        if (trim((string) ($page['title'] ?? '')) === '') {
            self::add($issues, 'warning', 'page_title_empty', '页面标题未填写', '建议填写清晰的页面标题，便于后台识别和浏览器展示。', 'page.title', '在页面设置中填写标题');
        }
        if (trim((string) ($page['seo_title'] ?? '')) === '') {
            self::add($issues, 'warning', 'seo_title_empty', 'SEO 标题未填写', '搜索结果可能直接使用页面标题，建议单独填写更准确的 SEO 标题。', 'page.seo_title', '在 SEO 设置中填写标题');
        }
        if (trim((string) ($page['seo_description'] ?? '')) === '') {
            self::add($issues, 'warning', 'seo_description_empty', 'SEO 描述未填写', '缺少页面摘要会降低搜索结果的可读性。', 'page.seo_description', '填写 80–160 字页面描述');
        }
        if (empty($page['mobile_enabled'])) {
            self::add($issues, 'warning', 'mobile_disabled', '移动端页面已关闭', '手机访客将无法使用独立的移动端装修配置。', 'page.mobile_enabled', '开启移动端站点');
        }

        $mobileWidth = intval($page['mobile_page_width'] ?? 375);
        if ($mobileWidth < 320 || $mobileWidth > 480) {
            self::add($issues, 'warning', 'mobile_width_unusual', '移动端画布宽度异常', '当前宽度为 ' . $mobileWidth . 'px，常用范围为 320–480px。', 'page.mobile_page_width', '调整到 375px 或 390px');
        }

        $deletedCount = count($page['frontend_deleted_paths'] ?? []) + count($page['frontend_mobile_deleted_paths'] ?? []);
        if ($deletedCount > 50) {
            self::add($issues, 'warning', 'too_many_deleted_blocks', '删除记录较多', '当前累计 ' . $deletedCount . ' 条区块删除记录，后续模板升级可能较难维护。', 'page.frontend_deleted_paths', '考虑恢复模板后重新整理页面');
        }

        self::inspectExtras($extras, $issues, $db);
        self::inspectWidgets($page, $issues);
        self::inspectLocalAssets($layout, $issues, $rootPath);

        $counts = ['error' => 0, 'warning' => 0, 'info' => 0];
        foreach ($issues as $issue) {
            $counts[$issue['severity']]++;
        }

        $score = max(0, 100 - $counts['error'] * 30 - $counts['warning'] * 6 - $counts['info'] * 2);
        return [
            'can_publish' => $counts['error'] === 0,
            'score' => $score,
            'grade' => $score >= 90 ? '优秀' : ($score >= 75 ? '良好' : ($score >= 60 ? '待优化' : '需修复')),
            'counts' => $counts,
            'issues' => $issues,
            'checked_at' => time(),
        ];
    }

    public static function invalidLayoutReport($message)
    {
        $issues = [];
        self::add($issues, 'error', 'layout_invalid', '布局安全校验未通过', (string) $message, 'layout', '修复提示中的布局或内容问题');
        return [
            'can_publish' => false,
            'score' => 0,
            'grade' => '需修复',
            'counts' => ['error' => 1, 'warning' => 0, 'info' => 0],
            'issues' => $issues,
            'checked_at' => time(),
        ];
    }

    private static function inspectExtras(array $extras, array &$issues, $db)
    {
        $ids = [];
        foreach ($extras as $index => $extra) {
            $position = 'page.frontend_extra_sections.' . $index;
            if (!is_array($extra)) {
                self::add($issues, 'error', 'extra_invalid', '独立模块结构无效', '第 ' . ($index + 1) . ' 个独立模块不是有效对象。', $position, '删除并重新添加该模块');
                continue;
            }

            $id = trim((string) ($extra['id'] ?? ''));
            $type = trim((string) ($extra['type'] ?? ''));
            $config = isset($extra['config']) && is_array($extra['config']) ? $extra['config'] : [];
            if ($id === '') {
                self::add($issues, 'error', 'extra_id_empty', '独立模块缺少标识', '第 ' . ($index + 1) . ' 个模块无法稳定保存和定位。', $position . '.id', '删除并重新添加该模块');
            } elseif (isset($ids[$id])) {
                self::add($issues, 'error', 'extra_id_duplicate', '独立模块标识重复', '模块标识 “' . $id . '” 出现多次，会造成编辑和渲染冲突。', $position . '.id', '删除重复模块后重新添加');
            } else {
                $ids[$id] = true;
            }

            if (!in_array($type, self::EXTRA_TYPES, true)) {
                self::add($issues, 'error', 'extra_type_unknown', '存在未知独立模块', '模块类型 “' . ($type ?: '空') . '” 当前版本无法识别。', $position . '.type', '删除该模块或替换为受支持组件');
                continue;
            }

            self::inspectExtraContent($type, $config, $position, $issues);
            self::inspectBusinessData($type, $config, $position, $issues, $db);
        }
    }

    private static function inspectExtraContent($type, array $config, $path, array &$issues)
    {
        if ($type === 'text') {
            $mode = (string) ($config['mode'] ?? 'paragraph');
            $value = $mode === 'rich' ? strip_tags((string) ($config['html'] ?? '')) : trim((string) ($config['text'] ?? ''));
            if (trim($value) === '') {
                self::add($issues, 'warning', 'text_empty', '文本模块内容为空', '空文本模块会在页面中形成无意义留白。', $path . '.config', '填写内容或删除该模块');
            }
        } elseif ($type === 'image' && trim((string) ($config['image_url'] ?? '')) === '') {
            self::add($issues, 'warning', 'image_empty', '图片模块未选择图片', '该模块发布后无法展示有效图片。', $path . '.config.image_url', '上传或选择图片');
        } elseif ($type === 'button') {
            if (trim((string) ($config['text'] ?? '')) === '') {
                self::add($issues, 'warning', 'button_text_empty', '按钮文字为空', '访客无法理解按钮用途。', $path . '.config.text', '填写按钮文字');
            }
            if (trim((string) ($config['action_value'] ?? '')) === '') {
                self::add($issues, 'warning', 'button_action_empty', '按钮没有交互目标', '点击按钮不会产生有效操作。', $path . '.config.action_value', '填写链接、电话、邮箱或锚点');
            }
        } elseif ($type === 'image_text') {
            if (trim((string) ($config['image_url'] ?? '')) === '') {
                self::add($issues, 'warning', 'image_text_image_empty', '图文模块缺少图片', '图文布局会失去主要视觉内容。', $path . '.config.image_url', '上传图文配图');
            }
            if (trim((string) ($config['heading'] ?? '')) === '') {
                self::add($issues, 'warning', 'image_text_heading_empty', '图文模块缺少标题', '访客难以快速理解模块主题。', $path . '.config.heading', '填写模块主标题');
            }
        } elseif (in_array($type, ['multi_image', 'gallery'], true) && trim((string) ($config['items'] ?? '')) === '') {
            self::add($issues, 'warning', $type . '_empty', '图片列表为空', '该模块没有可展示的图片项目。', $path . '.config.items', '至少添加一张图片');
        } elseif ($type === 'video_embed') {
            $video = trim((string) ($config['video_url'] ?? ''));
            $embed = trim((string) ($config['embed_html'] ?? ''));
            if ($video === '' && $embed === '') {
                self::add($issues, 'warning', 'video_empty', '视频模块缺少视频源', '发布后视频区域将无法播放。', $path . '.config', '上传视频或填写嵌入代码');
            }
        } elseif ($type === 'qrcode' && trim((string) ($config['image_url'] ?? $config['qrcode_url'] ?? '')) === '') {
            self::add($issues, 'warning', 'qrcode_empty', '二维码模块缺少图片', '访客无法完成扫码操作。', $path . '.config', '上传二维码图片');
        }
    }

    private static function inspectWidgets(array $page, array &$issues)
    {
        $widgets = isset($page['site_widgets']) && is_array($page['site_widgets']) ? $page['site_widgets'] : [];
        $configs = isset($page['site_widget_configs']) && is_array($page['site_widget_configs']) ? $page['site_widget_configs'] : [];
        if (!empty($widgets['background_music']) && trim((string) ($configs['background_music']['audio_url'] ?? '')) === '') {
            self::add($issues, 'warning', 'music_url_empty', '背景音乐已开启但未选择音频', '音乐挂件不会播放任何内容。', 'page.site_widget_configs.background_music.audio_url', '上传或填写音频地址');
        }
        if (!empty($widgets['floating_service'])) {
            $service = $configs['floating_service'] ?? [];
            $contacts = ['guestbook_url', 'phone', 'qq', 'wechat', 'email'];
            $hasContact = false;
            foreach ($contacts as $key) {
                $hasContact = $hasContact || trim((string) ($service[$key] ?? '')) !== '';
            }
            if (!$hasContact) {
                self::add($issues, 'warning', 'service_contact_empty', '浮动客服没有联系方式', '访客打开客服挂件后无法继续联系。', 'page.site_widget_configs.floating_service', '至少填写一种联系方式');
            }
        }
        if (!empty($widgets['top_ad']) && trim((string) ($configs['top_ad']['text'] ?? '')) === '') {
            self::add($issues, 'warning', 'top_ad_text_empty', '顶部广告文字为空', '顶部广告会占据空间但没有有效内容。', 'page.site_widget_configs.top_ad.text', '填写广告文案或关闭挂件');
        }
    }

    private static function inspectBusinessData($type, array $config, $path, array &$issues, $db)
    {
        if (!$db || !method_exists($db, 'fetch')) {
            return;
        }
        try {
            $count = null;
            if ($type === 'article_list') {
                $where = 'status = 1 AND deleted_at = 0';
                $params = [];
                if (intval($config['channel_id'] ?? 0) > 0) {
                    $where .= ' AND channel_id = ?';
                    $params[] = intval($config['channel_id']);
                }
                $count = self::countRows($db, 'article', $where, $params);
            } elseif ($type === 'product_list') {
                $where = 'status = 1 AND deleted_at = 0';
                $params = [];
                if (intval($config['channel_id'] ?? 0) > 0) {
                    $where .= ' AND channel_id = ?';
                    $params[] = intval($config['channel_id']);
                }
                $count = self::countRows($db, 'product', $where, $params);
            } elseif ($type === 'carousel' && (string) ($config['data_source'] ?? 'backend') !== 'custom') {
                $count = self::countRows($db, 'slide', 'status = 1', []);
            } elseif ($type === 'friend_links') {
                $count = self::countRows($db, 'link', 'status = 1', []);
            } elseif ($type === 'vote_form') {
                $voteId = intval($config['vote_id'] ?? 0);
                $count = $voteId > 0 ? self::countRows($db, 'vote', 'id = ? AND status = 1', [$voteId]) : 0;
            }
            if ($count === 0) {
                self::add($issues, 'warning', 'business_data_empty_' . $type, '业务模块暂无可展示数据', '“' . self::typeLabel($type) . '” 当前筛选条件没有已发布数据。', $path . '.config', '调整筛选条件或先发布后台内容');
            }
        } catch (\Throwable $error) {
            // 数据表版本差异不应阻断发布，跳过无法确认的业务数量。
        }
    }

    private static function countRows($db, $table, $where, array $params)
    {
        $row = $db->fetch('SELECT COUNT(*) AS total FROM ' . $db->table($table) . ' WHERE ' . $where, $params);
        return intval($row['total'] ?? 0);
    }

    private static function inspectLocalAssets(array $layout, array &$issues, $rootPath)
    {
        $rootPath = rtrim((string) $rootPath, '/\\');
        if ($rootPath === '') {
            return;
        }
        $urls = [];
        self::collectAssetUrls($layout, 'layout', $urls);
        foreach ($urls as $item) {
            $url = trim($item['url']);
            if ($url === '' || strpos($url, '{$') !== false || $url[0] !== '/') {
                continue;
            }
            $path = parse_url($url, PHP_URL_PATH);
            if (!$path || !preg_match('~\.(?:jpe?g|png|gif|webp|svg|ico|mp4|webm|mp3|wav|ogg|pdf|zip|rar)$~i', $path)) {
                continue;
            }
            $file = $rootPath . str_replace('/', DIRECTORY_SEPARATOR, $path);
            if (!is_file($file)) {
                self::add($issues, 'warning', 'asset_missing', '本地素材文件不存在', '资源地址 “' . $url . '” 在服务器上未找到对应文件。', $item['path'], '重新上传或更换素材地址');
            }
        }
    }

    private static function collectAssetUrls($value, $path, array &$urls)
    {
        if (!is_array($value)) {
            return;
        }
        foreach ($value as $key => $item) {
            $nextPath = $path . '.' . $key;
            if (is_array($item)) {
                self::collectAssetUrls($item, $nextPath, $urls);
                continue;
            }
            if (!is_string($item)) {
                continue;
            }
            $name = strtolower((string) $key);
            if (preg_match('/(?:image|img|src|litpic|poster|favicon|audio|video|file)(?:_url|_path)?$/', $name)) {
                $urls[] = ['url' => $item, 'path' => $nextPath];
            }
        }
    }

    private static function typeLabel($type)
    {
        $labels = [
            'article_list' => '文章列表',
            'product_list' => '产品列表',
            'carousel' => '轮播图',
            'friend_links' => '友情链接',
            'vote_form' => '投票表单',
        ];
        return $labels[$type] ?? $type;
    }

    private static function add(array &$issues, $severity, $code, $title, $message, $path, $fix)
    {
        $issues[] = [
            'severity' => $severity,
            'code' => $code,
            'title' => $title,
            'message' => $message,
            'path' => $path,
            'fix' => $fix,
        ];
    }
}
