{% extends "ewoooc_base.html" %}

{% block title %}CI/CD Dashboard - EwoooC{% endblock %}

{% block extra_head %}
    <link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css" rel="stylesheet">
{% endblock %}

{% block extra_css %}
    <style>
        .cicd-page {
            --success-color: var(--momo-success);
            --danger-color: var(--momo-danger);
            --warning-color: var(--momo-warning);
            --info-color: var(--momo-info);
            --uat-color: var(--momo-info);
            --prod-color: var(--momo-danger);
            color: var(--momo-text-primary);
            font-family: var(--momo-font-family);
        }

        .cicd-page * {
            letter-spacing: 0;
        }

        .dashboard-header {
            background: var(--momo-dot-grid), var(--momo-bg-surface);
            border: 1px solid var(--momo-border-strong);
            border-radius: var(--momo-radius-md);
            padding: 1.25rem;
            margin-bottom: 2rem;
            box-shadow: var(--momo-shadow-soft);
        }

        .dashboard-header h1 {
            margin: 0;
            color: var(--momo-text-primary);
            font-family: var(--momo-font-display);
            font-size: var(--momo-text-headline);
            font-weight: 800;
        }

        .status-indicator {
            width: 12px;
            height: 12px;
            border-radius: 50%;
            display: inline-block;
            margin-right: 8px;
            animation: pulse 2s infinite;
        }

        .status-indicator.healthy { background-color: var(--success-color); }
        .status-indicator.unhealthy { background-color: var(--danger-color); }
        .status-indicator.warning { background-color: var(--warning-color); }
        .status-indicator.running { background-color: var(--info-color); }

        @keyframes pulse {
            0%, 100% { opacity: 1; }
            50% { opacity: 0.5; }
        }

        .card {
            background: var(--momo-bg-surface);
            border: 1px solid var(--momo-border-light);
            border-radius: var(--momo-radius-md);
            backdrop-filter: none;
            transition: transform 0.3s, box-shadow 0.3s;
        }

        .card:hover {
            transform: translateY(-2px);
            box-shadow: var(--momo-shadow-soft);
        }

        .card-header {
            background: transparent;
            border-bottom: 1px solid var(--momo-border-light);
            color: var(--momo-text-primary);
            font-family: var(--momo-font-display);
            font-weight: 800;
        }

        /* Pipeline Flow */
        .pipeline-flow {
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 0;
            padding: 2rem;
            flex-wrap: wrap;
        }

        .pipeline-stage {
            display: flex;
            flex-direction: column;
            align-items: center;
            min-width: 120px;
        }

        .stage-box {
            width: 100px;
            height: 100px;
            border-radius: 12px;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            font-weight: 600;
            transition: all 0.3s;
            cursor: pointer;
        }

        .stage-box:hover {
            transform: scale(1.05);
        }

        .stage-box.success { background: var(--momo-success-bg); color: var(--momo-success-text); border: 1px solid var(--momo-success-border); }
        .stage-box.failed { background: var(--momo-danger-bg); color: var(--momo-danger-text); border: 1px solid var(--momo-danger-border); }
        .stage-box.running { background: var(--momo-info-bg); color: var(--momo-info-text); border: 1px solid var(--momo-info-border); animation: glow 1.5s infinite; }
        .stage-box.pending { background: var(--momo-bg-subtle); color: var(--momo-text-secondary); border: 1px solid var(--momo-border-light); }

        @keyframes glow {
            0%, 100% { box-shadow: 0 0 5px rgba(0, 123, 255, 0.5); }
            50% { box-shadow: 0 0 20px rgba(0, 123, 255, 0.8); }
        }

        .stage-icon {
            font-size: 1.8rem;
            margin-bottom: 0.25rem;
        }

        .stage-name {
            font-size: 0.85rem;
            text-transform: uppercase;
        }

        .stage-duration {
            font-size: 0.75rem;
            opacity: 0.8;
            margin-top: 0.5rem;
        }

        .pipeline-arrow {
            width: 40px;
            height: 2px;
            background: var(--momo-border-strong);
            position: relative;
        }

        .pipeline-arrow::after {
            content: '';
            position: absolute;
            right: -5px;
            top: -4px;
            border: 5px solid transparent;
            border-left-color: var(--momo-border-strong);
        }

        /* Environment Cards */
        .env-card {
            border-left: 4px solid;
            transition: all 0.3s;
        }

        .env-card.uat { border-left-color: var(--uat-color); }
        .env-card.prod { border-left-color: var(--prod-color); }

        .env-icon {
            font-size: 2rem;
            margin-right: 1rem;
        }

        .env-details {
            font-size: 0.9rem;
        }

        .pod-status {
            display: flex;
            align-items: center;
            padding: 0.5rem;
            background: var(--momo-bg-paper);
            border: 1px solid var(--momo-border-light);
            border-radius: var(--momo-radius-sm);
            margin-bottom: 0.5rem;
        }

        .pod-status .pod-name {
            flex: 1;
            font-family: monospace;
            font-size: 0.85rem;
        }

        .pod-status .pod-ready {
            font-size: 0.8rem;
            padding: 0.2rem 0.5rem;
            border-radius: 4px;
            margin-left: 0.5rem;
        }

        .pod-status .pod-ready.healthy { background: rgba(40, 167, 69, 0.3); }
        .pod-status .pod-ready.unhealthy { background: rgba(220, 53, 69, 0.3); }

        /* Pipeline History */
        .pipeline-item {
            display: flex;
            align-items: center;
            padding: 0.75rem;
            background: var(--momo-bg-paper);
            border: 1px solid var(--momo-border-light);
            border-radius: var(--momo-radius-sm);
            margin-bottom: 0.5rem;
            transition: background 0.3s;
        }

        .pipeline-item:hover {
            background: var(--momo-page-accent-soft);
        }

        .pipeline-id {
            font-family: monospace;
            font-weight: 600;
            width: 80px;
        }

        .pipeline-status {
            width: 30px;
            text-align: center;
        }

        .pipeline-info {
            flex: 1;
            margin-left: 1rem;
        }

        .pipeline-commit {
            font-size: 0.85rem;
            color: var(--momo-text-secondary);
        }

        .pipeline-time {
            font-size: 0.8rem;
            color: var(--momo-text-tertiary);
        }

        /* Summary Stats */
        .stat-box {
            text-align: center;
            padding: 1.5rem;
        }

        .stat-value {
            font-size: 2.5rem;
            font-weight: 700;
            line-height: 1;
        }

        .stat-label {
            font-size: 0.85rem;
            color: rgba(255, 255, 255, 0.7);
            margin-top: 0.5rem;
        }

        /* Action Buttons */
        .action-btn {
            display: flex;
            align-items: center;
            justify-content: center;
            padding: 0.75rem 1rem;
            border-radius: 8px;
            font-weight: 500;
            transition: all 0.3s;
            border: none;
            cursor: pointer;
        }

        .action-btn i {
            margin-right: 0.5rem;
        }

        .action-btn.primary {
            background: var(--momo-page-accent);
            border: 1px solid var(--momo-page-accent-dark);
            color: var(--momo-page-inverse);
        }

        .action-btn.danger {
            background: var(--momo-danger);
            border: 1px solid var(--momo-danger);
            color: var(--momo-text-inverse);
        }

        .action-btn:hover {
            transform: translateY(-2px);
            box-shadow: none;
        }

        /* Loading State */
        .loading-spinner {
            display: flex;
            justify-content: center;
            align-items: center;
            padding: 2rem;
        }

        .spinner {
            width: 40px;
            height: 40px;
            border: 3px solid var(--momo-border-light);
            border-top-color: var(--momo-page-accent);
            border-radius: 50%;
            animation: spin 1s linear infinite;
        }

        @keyframes spin {
            to { transform: rotate(360deg); }
        }

        /* Real-time indicator */
        .realtime-badge {
            display: inline-flex;
            align-items: center;
            font-size: 0.75rem;
            padding: 0.25rem 0.75rem;
            background: rgba(40, 167, 69, 0.2);
            border: 1px solid rgba(40, 167, 69, 0.5);
            border-radius: 20px;
            color: #28a745;
        }

        .realtime-dot {
            width: 6px;
            height: 6px;
            background: #28a745;
            border-radius: 50%;
            margin-right: 6px;
            animation: pulse 1s infinite;
        }

        /* Response time badge */
        .response-time {
            font-size: 0.75rem;
            padding: 0.2rem 0.5rem;
            border-radius: 4px;
            background: rgba(255, 255, 255, 0.1);
        }

        .response-time.fast { color: #28a745; }
        .response-time.medium { color: #ffc107; }
        .response-time.slow { color: #dc3545; }

        /* Issues Panel */
        .issues-panel {
            max-height: 400px;
            overflow-y: auto;
        }

        .issue-item {
            display: flex;
            align-items: flex-start;
            padding: 1rem;
            background: rgba(255, 255, 255, 0.03);
            border-radius: 8px;
            margin-bottom: 0.75rem;
            border-left: 4px solid;
        }

        .issue-item.critical { border-left-color: #dc3545; background: rgba(220, 53, 69, 0.1); }
        .issue-item.warning { border-left-color: #ffc107; background: rgba(255, 193, 7, 0.1); }

        .issue-icon {
            font-size: 1.5rem;
            margin-right: 1rem;
        }

        .issue-content {
            flex: 1;
        }

        .issue-title {
            font-weight: 600;
            margin-bottom: 0.25rem;
        }

        .issue-detail {
            font-size: 0.85rem;
            color: var(--momo-text-secondary);
        }

        .issue-suggestion {
            font-size: 0.8rem;
            color: var(--momo-info-text);
            margin-top: 0.5rem;
        }

        .issue-actions {
            margin-left: 1rem;
        }

        .error-log-preview {
            font-family: monospace;
            font-size: 0.75rem;
            background: var(--momo-bg-paper);
            border: 1px solid var(--momo-border-light);
            padding: 0.5rem;
            border-radius: 4px;
            margin-top: 0.5rem;
            max-height: 100px;
            overflow-y: auto;
            white-space: pre-wrap;
            color: var(--momo-danger-text);
        }

        /* Stage Error Tooltip */
        .stage-error {
            font-size: 0.7rem;
            color: var(--momo-danger-text);
            margin-top: 0.5rem;
            max-width: 120px;
            word-wrap: break-word;
        }

        /* Fix Button */
        .btn-fix {
            background: var(--momo-info);
            border: 1px solid var(--momo-info);
            color: var(--momo-text-inverse);
            padding: 0.375rem 0.75rem;
            border-radius: 4px;
            font-size: 0.8rem;
            cursor: pointer;
            transition: all 0.3s;
        }

        .btn-fix:hover {
            transform: translateY(-2px);
            background: var(--momo-info-text);
            border-color: var(--momo-info-text);
        }

        .btn-fix:disabled {
            opacity: 0.5;
            cursor: not-allowed;
        }

        /* Environment Error */
        .env-error {
            background: rgba(220, 53, 69, 0.2);
            border: 1px solid rgba(220, 53, 69, 0.5);
            border-radius: 8px;
            padding: 0.75rem;
            margin-top: 0.5rem;
        }

        .env-error-title {
            color: #dc3545;
            font-weight: 600;
            font-size: 0.9rem;
        }

        .env-error-detail {
            font-family: monospace;
            font-size: 0.8rem;
            color: rgba(255, 255, 255, 0.8);
            margin-top: 0.25rem;
        }
    </style>
{% endblock %}

{% block content %}
<div class="cicd-page">
    <!-- Header -->
    <div class="dashboard-header">
        <div class="container">
            <div class="d-flex justify-content-between align-items-center">
                <div>
                    <h1><i class="bi bi-rocket-takeoff me-2"></i>CI/CD Dashboard</h1>
                    <p class="mb-0 opacity-75">MOMO Pro System - 持續整合與部署監控</p>
                </div>
                <div class="d-flex align-items-center gap-3">
                    <div class="realtime-badge">
                        <span class="realtime-dot"></span>
                        即時更新
                    </div>
                    <span id="lastUpdate" class="text-white-50 small"></span>
                </div>
            </div>
        </div>
    </div>

    <div class="container">
        <!-- Summary Stats -->
        <div class="row mb-4">
            <div class="col-md-3">
                <div class="card">
                    <div class="stat-box">
                        <div class="stat-value text-success" id="statSuccess">-</div>
                        <div class="stat-label">成功率 (%)</div>
                    </div>
                </div>
            </div>
            <div class="col-md-3">
                <div class="card">
                    <div class="stat-box">
                        <div class="stat-value text-info" id="statToday">-</div>
                        <div class="stat-label">今日部署</div>
                    </div>
                </div>
            </div>
            <div class="col-md-3">
                <div class="card">
                    <div class="stat-box">
                        <div id="uatStatus" class="stat-value">-</div>
                        <div class="stat-label">UAT 狀態</div>
                    </div>
                </div>
            </div>
            <div class="col-md-3">
                <div class="card">
                    <div class="stat-box">
                        <div id="prodStatus" class="stat-value">-</div>
                        <div class="stat-label">PROD 狀態</div>
                    </div>
                </div>
            </div>
        </div>

        <!-- Pipeline Flow -->
        <div class="card mb-4">
            <div class="card-header d-flex justify-content-between align-items-center">
                <span><i class="bi bi-diagram-3 me-2"></i>最新 Pipeline</span>
                <span id="pipelineId" class="badge bg-secondary">#--</span>
            </div>
            <div class="card-body">
                <div class="pipeline-flow" id="pipelineFlow">
                    <div class="loading-spinner">
                        <div class="spinner"></div>
                    </div>
                </div>
            </div>
        </div>

        <div class="row">
            <!-- Environments -->
            <div class="col-lg-6 mb-4">
                <div class="card h-100">
                    <div class="card-header">
                        <i class="bi bi-hdd-stack me-2"></i>環境狀態
                    </div>
                    <div class="card-body" id="environmentsContainer">
                        <div class="loading-spinner">
                            <div class="spinner"></div>
                        </div>
                    </div>
                </div>
            </div>

            <!-- Quick Actions -->
            <div class="col-lg-6 mb-4">
                <div class="card h-100">
                    <div class="card-header">
                        <i class="bi bi-lightning me-2"></i>快速操作
                    </div>
                    <div class="card-body">
                        <div class="row g-3">
                            <div class="col-6">
                                <button class="action-btn primary w-100" onclick="triggerDeploy('uat')">
                                    <i class="bi bi-cloud-upload"></i>部署 UAT
                                </button>
                            </div>
                            <div class="col-6">
                                <button class="action-btn primary w-100" onclick="triggerDeploy('prod')">
                                    <i class="bi bi-cloud-upload"></i>部署 PROD
                                </button>
                            </div>
                            <div class="col-6">
                                <button class="action-btn danger w-100" onclick="triggerRollback('uat')">
                                    <i class="bi bi-arrow-counterclockwise"></i>回滾 UAT
                                </button>
                            </div>
                            <div class="col-6">
                                <button class="action-btn danger w-100" onclick="triggerRollback('prod')">
                                    <i class="bi bi-arrow-counterclockwise"></i>回滾 PROD
                                </button>
                            </div>
                        </div>

                        <hr class="my-4 border-secondary">

                        <div class="row g-3">
                            <div class="col-12">
                                <a href="http://192.168.0.110:8929/root/momo-pro-system/-/pipelines" target="_blank" class="btn btn-outline-light w-100">
                                    <i class="bi bi-box-arrow-up-right me-2"></i>開啟 GitLab Pipelines
                                </a>
                            </div>
                            <div class="col-6">
                                <a href="http://192.168.0.110:30030" target="_blank" class="btn btn-outline-info w-100">
                                    <i class="bi bi-graph-up me-2"></i>Grafana
                                </a>
                            </div>
                            <div class="col-6">
                                <a href="http://192.168.0.110:5678" target="_blank" class="btn btn-outline-warning w-100">
                                    <i class="bi bi-diagram-2 me-2"></i>n8n
                                </a>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <!-- Issues Panel -->
        <div class="card mb-4" id="issuesPanelCard" style="display: none;">
            <div class="card-header d-flex justify-content-between align-items-center bg-danger bg-opacity-25">
                <span><i class="bi bi-exclamation-triangle me-2"></i>問題與告警 <span id="issuesCount" class="badge bg-danger ms-2">0</span></span>
                <div>
                    <button class="btn btn-sm btn-outline-warning me-2" onclick="runDiagnosis()">
                        <i class="bi bi-search me-1"></i>診斷
                    </button>
                    <button class="btn btn-sm btn-outline-info" onclick="triggerFullRepair()">
                        <i class="bi bi-wrench me-1"></i>一鍵修復
                    </button>
                </div>
            </div>
            <div class="card-body issues-panel" id="issuesContainer">
            </div>
        </div>

        <!-- Pipeline History -->
        <div class="card mb-4">
            <div class="card-header d-flex justify-content-between align-items-center">
                <span><i class="bi bi-clock-history me-2"></i>部署歷史</span>
                <a href="http://192.168.0.110:8929/root/momo-pro-system/-/pipelines" target="_blank" class="btn btn-sm btn-outline-light">
                    查看全部
                </a>
            </div>
            <div class="card-body" id="pipelineHistory">
                <div class="loading-spinner">
                    <div class="spinner"></div>
                </div>
            </div>
        </div>
    </div>

    <!-- Toast Notifications -->
    <div class="toast-container position-fixed bottom-0 end-0 p-3">
        <div id="notificationToast" class="toast" role="alert">
            <div class="toast-header">
                <strong class="me-auto" id="toastTitle">通知</strong>
                <button type="button" class="btn-close" data-bs-dismiss="toast"></button>
            </div>
            <div class="toast-body" id="toastMessage"></div>
        </div>
    </div>
</div>

{% endblock %}

{% block extra_js %}
    <script>
        // 配置
        const REFRESH_INTERVAL = 10000; // 10 秒刷新
        let refreshTimer = null;

        // 初始化
        document.addEventListener('DOMContentLoaded', () => {
            loadDashboard();
            startAutoRefresh();
        });

        // 自動刷新
        function startAutoRefresh() {
            refreshTimer = setInterval(loadDashboard, REFRESH_INTERVAL);
        }

        // 載入 Dashboard 數據
        async function loadDashboard() {
            try {
                const response = await fetch('/api/cicd/status');
                const data = await response.json();

                if (data.success) {
                    updateSummary(data.summary);
                    updatePipelineFlow(data.latest_pipeline, data.latest_jobs);
                    updateEnvironments(data.environments);
                    updatePipelineHistory(data.pipelines);
                    updateIssuesPanel(data.issues);
                    updateLastUpdate();
                }
            } catch (error) {
                console.error('Failed to load dashboard:', error);
            }
        }

        // 更新問題面板
        function updateIssuesPanel(issues) {
            const panel = document.getElementById('issuesPanelCard');
            const container = document.getElementById('issuesContainer');
            const countBadge = document.getElementById('issuesCount');

            if (!issues || issues.length === 0) {
                panel.style.display = 'none';
                return;
            }

            panel.style.display = 'block';
            countBadge.textContent = issues.length;

            let html = '';
            issues.forEach(issue => {
                const severityClass = issue.severity === 'critical' ? 'critical' : 'warning';
                const icon = issue.severity === 'critical' ? '🚨' : '⚠️';

                html += `
                    <div class="issue-item ${severityClass}">
                        <span class="issue-icon">${icon}</span>
                        <div class="issue-content">
                            <div class="issue-title">${escapeHtml(issue.message)}</div>
                            <div class="issue-detail">
                                ${issue.type === 'job' ? `<span class="badge bg-secondary me-1">${issue.stage}</span>` : ''}
                                ${issue.type === 'runtime' ? `<span class="badge bg-info me-1">${issue.environment?.toUpperCase()}</span>` : ''}
                                ${issue.error ? `<br><code>${escapeHtml(issue.error.substring(0, 100))}</code>` : ''}
                            </div>
                            ${issue.fix_suggestion ? `<div class="issue-suggestion">💡 ${escapeHtml(issue.fix_suggestion)}</div>` : ''}
                            ${issue.error_log ? `<div class="error-log-preview">${escapeHtml(issue.error_log.substring(0, 300))}</div>` : ''}
                        </div>
                        <div class="issue-actions">
                            ${issue.auto_fixable ? `
                                <button class="btn-fix" onclick="triggerAutoFix('${issue.fix_action}', '${issue.environment || 'uat'}')">
                                    <i class="bi bi-wrench me-1"></i>修復
                                </button>
                            ` : ''}
                        </div>
                    </div>
                `;
            });

            container.innerHTML = html;
        }

        // 更新摘要統計
        function updateSummary(summary) {
            document.getElementById('statSuccess').textContent = summary.success_rate || 0;
            document.getElementById('statToday').textContent = summary.total_pipelines_today || 0;

            const uatStatus = document.getElementById('uatStatus');
            const prodStatus = document.getElementById('prodStatus');

            if (summary.uat_healthy) {
                uatStatus.innerHTML = '<span class="text-success">✅ 健康</span>';
            } else {
                uatStatus.innerHTML = '<span class="text-danger">❌ 異常</span>';
            }

            if (summary.prod_healthy) {
                prodStatus.innerHTML = '<span class="text-success">✅ 健康</span>';
            } else {
                prodStatus.innerHTML = '<span class="text-danger">❌ 異常</span>';
            }
        }

        // 更新 Pipeline 流程圖
        function updatePipelineFlow(latestPipeline, latestJobs) {
            const container = document.getElementById('pipelineFlow');
            const pipelineIdEl = document.getElementById('pipelineId');

            if (!latestPipeline) {
                container.innerHTML = '<p class="text-center text-muted">暫無 Pipeline 數據</p>';
                return;
            }

            pipelineIdEl.textContent = `#${latestPipeline.id}`;

            // 如果已有 jobs 數據，直接使用
            if (latestJobs && latestJobs.length > 0) {
                const stages = groupJobsByStage(latestJobs);
                renderPipelineFlowWithJobs(container, stages);
            } else {
                // 否則取得 Pipeline 的 Jobs
                fetch(`/api/cicd/pipeline/${latestPipeline.id}`)
                    .then(res => res.json())
                    .then(data => {
                        if (data.success && data.stages) {
                            renderPipelineFlow(container, data.stages);
                        }
                    })
                    .catch(err => {
                        console.error('Failed to load pipeline details:', err);
                        renderSimplePipelineFlow(container, latestPipeline);
                    });
            }
        }

        // 將 Jobs 按 Stage 分組
        function groupJobsByStage(jobs) {
            const stageOrder = ['test', 'build', 'deploy'];
            const stages = {};

            jobs.forEach(job => {
                const stageName = job.stage || 'unknown';
                if (!stages[stageName]) {
                    stages[stageName] = {
                        name: stageName,
                        status: 'pending',
                        jobs: [],
                        duration: 0,
                        error_info: null
                    };
                }
                stages[stageName].jobs.push(job);

                // 更新 stage 狀態
                if (job.status === 'failed') {
                    stages[stageName].status = 'failed';
                    stages[stageName].error_info = job.error_info;
                    stages[stageName].failure_reason = job.failure_reason;
                } else if (job.status === 'running' && stages[stageName].status !== 'failed') {
                    stages[stageName].status = 'running';
                } else if (job.status === 'success' && stages[stageName].status === 'pending') {
                    stages[stageName].status = 'success';
                }

                if (job.duration) {
                    stages[stageName].duration += job.duration;
                }
            });

            // 按順序返回
            return stageOrder.map(name => stages[name]).filter(s => s);
        }

        // 渲染 Pipeline 流程圖（帶 Job 詳情）
        function renderPipelineFlowWithJobs(container, stages) {
            let html = '';

            stages.forEach((stage, index) => {
                if (index > 0) {
                    html += '<div class="pipeline-arrow"></div>';
                }

                const errorMsg = stage.error_info?.message || stage.failure_reason || '';
                const fixSuggestion = stage.error_info?.fix_suggestion || '';

                html += `
                    <div class="pipeline-stage">
                        <div class="stage-box ${stage.status}" title="${escapeHtml(errorMsg)}">
                            <span class="stage-icon">${getStatusIcon(stage.status)}</span>
                            <span class="stage-name">${stage.name}</span>
                        </div>
                        <span class="stage-duration">${formatDuration(stage.duration)}</span>
                        ${stage.status === 'failed' ? `
                            <div class="stage-error" title="${escapeHtml(fixSuggestion)}">
                                ${escapeHtml(errorMsg.substring(0, 30))}${errorMsg.length > 30 ? '...' : ''}
                            </div>
                        ` : ''}
                    </div>
                `;
            });

            container.innerHTML = html;
        }

        // 渲染 Pipeline 流程圖
        function renderPipelineFlow(container, stages) {
            let html = '';

            stages.forEach((stage, index) => {
                if (index > 0) {
                    html += '<div class="pipeline-arrow"></div>';
                }

                html += `
                    <div class="pipeline-stage">
                        <div class="stage-box ${stage.status}">
                            <span class="stage-icon">${stage.status_icon}</span>
                            <span class="stage-name">${stage.name}</span>
                        </div>
                        <span class="stage-duration">${formatDuration(stage.duration)}</span>
                    </div>
                `;
            });

            container.innerHTML = html;
        }

        // 簡易 Pipeline 流程圖
        function renderSimplePipelineFlow(container, pipeline) {
            const stages = ['test', 'build', 'deploy'];
            let html = '';

            stages.forEach((stage, index) => {
                if (index > 0) {
                    html += '<div class="pipeline-arrow"></div>';
                }

                const status = index === 2 && pipeline.status === 'success' ? 'success' :
                               index < 2 && pipeline.status !== 'failed' ? 'success' :
                               pipeline.status === 'running' && index === 2 ? 'running' :
                               pipeline.status === 'failed' ? 'failed' : 'pending';

                html += `
                    <div class="pipeline-stage">
                        <div class="stage-box ${status}">
                            <span class="stage-icon">${getStatusIcon(status)}</span>
                            <span class="stage-name">${stage}</span>
                        </div>
                    </div>
                `;
            });

            container.innerHTML = html;
        }

        // 更新環境狀態
        function updateEnvironments(environments) {
            const container = document.getElementById('environmentsContainer');
            let html = '';

            for (const [envId, env] of Object.entries(environments)) {
                const healthClass = env.healthy ? 'healthy' : 'unhealthy';
                const responseClass = env.response_time < 500 ? 'fast' :
                                     env.response_time < 1000 ? 'medium' : 'slow';

                html += `
                    <div class="env-card card mb-3 ${envId}">
                        <div class="card-body">
                            <div class="d-flex align-items-center mb-3">
                                <span class="env-icon">${env.icon}</span>
                                <div>
                                    <h5 class="mb-0">${env.name} - ${env.label}</h5>
                                    <a href="${env.url}" target="_blank" class="text-muted small">${env.url}</a>
                                </div>
                                <div class="ms-auto d-flex align-items-center">
                                    <span class="status-indicator ${healthClass}"></span>
                                    <span class="${env.healthy ? 'text-success' : 'text-danger'}">${env.healthy ? '健康' : '異常'}</span>
                                    ${env.response_time ? `<span class="response-time ${responseClass} ms-2">${env.response_time}ms</span>` : ''}
                                </div>
                            </div>

                            ${env.version ? `<div class="mb-2"><small class="text-muted">版本:</small> <code>${env.version}</code></div>` : ''}

                            ${!env.healthy && env.error ? `
                                <div class="env-error">
                                    <div class="env-error-title">❌ 連線錯誤</div>
                                    <div class="env-error-detail">${escapeHtml(env.error)}</div>
                                    <button class="btn-fix mt-2" onclick="triggerAutoFix('diagnose', '${envId}')">
                                        <i class="bi bi-search me-1"></i>診斷服務
                                    </button>
                                </div>
                            ` : ''}

                            <div class="env-details mt-2">
                                <strong class="d-block mb-2">Runtime 狀態:</strong>
                                ${renderPods(env.pods, envId)}
                            </div>
                        </div>
                    </div>
                `;
            }

            container.innerHTML = html;
        }

        // 渲染 runtime 狀態
        function renderPods(pods, envId) {
            if (!pods || pods.length === 0) {
                return '<p class="text-muted small mb-0">Docker Compose runtime；舊叢集資訊不適用</p>';
            }

            return pods.map(pod => `
                <div class="pod-status">
                    <span class="status-indicator ${pod.healthy ? 'healthy' : 'unhealthy'}"></span>
                    <span class="pod-name">${pod.name}</span>
                    <span class="pod-ready ${pod.healthy ? 'healthy' : 'unhealthy'}">${pod.ready}</span>
                    <span class="text-muted small ms-2">${pod.age}</span>
                    ${pod.restarts > 0 ? `<span class="badge bg-warning ms-2">${pod.restarts} 重啟</span>` : ''}
                    ${!pod.healthy ? `<span class="badge bg-danger ms-2">${pod.status}</span>` : ''}
                </div>
            `).join('');
        }

        // 更新 Pipeline 歷史
        function updatePipelineHistory(pipelines) {
            const container = document.getElementById('pipelineHistory');

            if (!pipelines || pipelines.length === 0) {
                container.innerHTML = '<p class="text-center text-muted">暫無 Pipeline 記錄</p>';
                return;
            }

            let html = '';
            pipelines.slice(0, 10).forEach(p => {
                html += `
                    <div class="pipeline-item">
                        <span class="pipeline-status">${p.status_icon}</span>
                        <span class="pipeline-id">#${p.id}</span>
                        <div class="pipeline-info">
                            <div class="pipeline-commit">${p.ref} @ ${p.sha}</div>
                            <div class="pipeline-time">${formatTime(p.created_at)} ${p.duration ? `• ${p.duration}` : ''}</div>
                        </div>
                        <a href="${p.web_url}" target="_blank" class="btn btn-sm btn-outline-secondary">
                            <i class="bi bi-box-arrow-up-right"></i>
                        </a>
                    </div>
                `;
            });

            container.innerHTML = html;
        }

        // 更新最後更新時間
        function updateLastUpdate() {
            const el = document.getElementById('lastUpdate');
            const now = new Date();
            el.textContent = `最後更新: ${now.toLocaleTimeString('zh-TW')}`;
        }

        // 觸發部署
        async function triggerDeploy(env) {
            if (!confirm(`確定要觸發 ${env.toUpperCase()} 部署嗎？`)) return;

            try {
                const response = await fetch('/api/cicd/deploy', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ environment: env })
                });

                const data = await response.json();
                showNotification(data.success ? '成功' : '失敗', data.message || data.error);

                if (data.success) {
                    setTimeout(loadDashboard, 2000);
                }
            } catch (error) {
                showNotification('錯誤', '無法觸發部署');
            }
        }

        // 觸發回滾
        async function triggerRollback(env) {
            if (!confirm(`確定要回滾 ${env.toUpperCase()} 嗎？這將恢復到上一個版本。`)) return;

            try {
                const response = await fetch('/api/cicd/rollback', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ environment: env })
                });

                const data = await response.json();
                showNotification(data.success ? '成功' : '失敗', data.message || data.error);

                if (data.success) {
                    setTimeout(loadDashboard, 2000);
                }
            } catch (error) {
                showNotification('錯誤', '無法執行回滾');
            }
        }

        // 顯示通知
        function showNotification(title, message, isError = false) {
            document.getElementById('toastTitle').textContent = title;
            document.getElementById('toastMessage').textContent = message;
            const toastEl = document.getElementById('notificationToast');
            if (isError) {
                toastEl.classList.add('bg-danger');
            } else {
                toastEl.classList.remove('bg-danger');
            }
            const toast = new bootstrap.Toast(toastEl);
            toast.show();
        }

        // 自動修復
        async function triggerAutoFix(action, env) {
            if (!confirm(`確定要執行自動修復 (${action}) 嗎？`)) return;

            showNotification('執行中', `正在執行 ${action}...`);

            try {
                const response = await fetch('/api/cicd/auto-fix', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ action, environment: env })
                });

                const data = await response.json();

                if (data.success) {
                    showNotification('✅ 修復完成', data.message);
                    setTimeout(loadDashboard, 3000);
                } else {
                    showNotification('❌ 修復失敗', data.error, true);
                }
            } catch (error) {
                showNotification('❌ 錯誤', '無法執行自動修復: ' + error, true);
            }
        }

        // 完整修復
        async function triggerFullRepair() {
            const env = prompt('請輸入要修復的環境 (uat 或 prod):', 'uat');
            if (!env || !['uat', 'prod'].includes(env)) {
                showNotification('取消', '無效的環境');
                return;
            }

            if (!confirm(`確定要對 ${env.toUpperCase()} 執行完整修復嗎？\n這會重啟 Registry 並執行 runtime 診斷，不會重啟舊叢集。`)) return;

            showNotification('執行中', '正在執行完整修復...');

            try {
                const response = await fetch('/api/cicd/auto-fix', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ action: 'full_repair', environment: env })
                });

                const data = await response.json();

                if (data.success) {
                    showNotification('✅ 完整修復完成', `已執行 ${data.results?.length || 0} 個修復動作`);
                    setTimeout(loadDashboard, 5000);
                } else {
                    showNotification('❌ 修復失敗', data.error, true);
                }
            } catch (error) {
                showNotification('❌ 錯誤', '無法執行完整修復: ' + error, true);
            }
        }

        // 診斷
        async function runDiagnosis() {
            const env = prompt('請輸入要診斷的環境 (uat 或 prod):', 'uat');
            if (!env || !['uat', 'prod'].includes(env)) {
                showNotification('取消', '無效的環境');
                return;
            }

            showNotification('執行中', `正在診斷 ${env.toUpperCase()} 環境...`);

            try {
                const response = await fetch('/api/cicd/diagnose', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ environment: env })
                });

                const data = await response.json();

                if (data.success) {
                    const diagnosis = data.diagnosis;
                    let message = `診斷結果: ${diagnosis.summary?.overall_status?.toUpperCase()}\n`;
                    message += `失敗: ${diagnosis.summary?.failed_count || 0}, 警告: ${diagnosis.summary?.warning_count || 0}\n\n`;

                    diagnosis.checks?.forEach(check => {
                        const icon = check.status === 'ok' ? '✅' : (check.status === 'warning' ? '⚠️' : '❌');
                        message += `${icon} ${check.name}: ${check.status}\n`;
                    });

                    if (diagnosis.summary?.recommendations?.length > 0) {
                        message += '\n建議動作:\n';
                        diagnosis.summary.recommendations.forEach(rec => {
                            message += `  • ${rec.description}\n`;
                        });
                    }

                    alert(message);
                } else {
                    showNotification('❌ 診斷失敗', data.error, true);
                }
            } catch (error) {
                showNotification('❌ 錯誤', '無法執行診斷: ' + error, true);
            }
        }

        // HTML 轉義
        function escapeHtml(text) {
            if (!text) return '';
            const div = document.createElement('div');
            div.textContent = text;
            return div.innerHTML;
        }

        // 輔助函數
        function getStatusIcon(status) {
            const icons = {
                'success': '✅', 'passed': '✅',
                'failed': '❌', 'running': '🔄',
                'pending': '⏳', 'canceled': '⛔'
            };
            return icons[status] || '❓';
        }

        function formatDuration(seconds) {
            if (!seconds) return '-';
            if (seconds < 60) return `${Math.round(seconds)}秒`;
            if (seconds < 3600) return `${Math.floor(seconds / 60)}分${Math.round(seconds % 60)}秒`;
            return `${Math.floor(seconds / 3600)}時${Math.floor((seconds % 3600) / 60)}分`;
        }

        function formatTime(isoString) {
            if (!isoString) return '-';
            const date = new Date(isoString);
            return date.toLocaleString('zh-TW', {
                month: '2-digit', day: '2-digit',
                hour: '2-digit', minute: '2-digit'
            });
        }
    </script>
{% endblock %}
