// ==UserScript==
// @name         Linux.do实时帖子 & AI 分析侧边栏
// @namespace    http://tampermonkey.net/
// @version      3.1.0
// @description  在 linux.do 页面左侧显示实时帖子列表并使用 Gemini API 进行分析总结，优化版
// @author       NullUser
// @match        https://linux.do/*
// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_notification
// @grant        GM_xmlhttpRequest
// @connect      generativelanguage.googleapis.com
// @connect      g.shatang.me
// @require      https://cdn.jsdelivr.net/npm/marked/marked.min.js
// ==/UserScript==

(function() {
    'use strict';

    class LinuxDoEnhancer {
        constructor() {
            // Default Configuration
            this.config = {
                POSTS_URL: '/posts.json',
                DEFAULT_POST_FETCH_INTERVAL: 30 * 1000, // Default: Fetch posts every 30 seconds
                DEFAULT_AI_ANALYZE_INTERVAL: 120 * 1000, // Default: Analyze posts every 2 minutes (120 seconds)
                SCROLL_INTERVAL: 60, // Scroll every 60ms for smoother effect
                SCROLL_AMOUNT: 1, // Scroll 1 pixel down each time
                MAX_DISPLAY_POSTS: 100, // Limit the number of posts displayed in the list
                AI_ANALYSIS_POST_COUNT: 15, // Number of recent posts to send to AI
                SIDEBAR_WIDTH: '320px', // Slightly wider for more info
                API_KEY_STORAGE_KEY: 'linuxdo_enhancer_gemini_api_key_v3',
                API_URL_STORAGE_KEY: 'linuxdo_enhancer_gemini_api_base_url_v3',
                FETCH_INTERVAL_STORAGE_KEY: 'linuxdo_enhancer_fetch_interval_v3',
                ANALYZE_INTERVAL_STORAGE_KEY: 'linuxdo_enhancer_analyze_interval_v3',
                // --- Configurable API Base URL ---
                // Use Google's official API: 'https://generativelanguage.googleapis.com/v1beta/models/'
                // Or your chosen proxy: 'https://g.shatang.me/v1beta/models/'
                GEMINI_API_BASE_URL: 'https://generativelanguage.googleapis.com/v1beta/models/', // Default to official
                GEMINI_MODEL: 'gemini-2.0-flash' // Or 'gemini-pro' or specific version like 'gemini-2.0-flash'
            };

            this.state = {
                rawPostData: [], // Store raw fetched posts
                // No longer need filteredPostData after removing search
                // filteredPostData: [],
                scrollIntervalId: null,
                isSidebarVisible: true,
                isFetchingPosts: false,
                isAnalyzing: false,
                isScrollingPaused: false,
                lastUpdateTime: null
            };

            this.domElements = {};
            this.intervals = {}; // Store fetch and analyze interval IDs

            // Ensure marked is available, fallback to simple passthrough if not loaded
            this.marked = typeof marked !== 'undefined' ? marked : { parse: (text) => text };


            this.init();
        }

        init() {
            this.injectStyles();
            this.waitForBody(() => {
                this.injectUI();
                // Wait for the main sidebar element to exist before initializing others
                this.waitForElement('#linuxdo-enhancer-sidebar', () => {
                    this.initElements();
                    this.setupEventListeners();
                    this.loadConfigAndStart(); // Load saved config and then start
                });
            });
        }

        // Helper to wait for a specific element to appear
        waitForElement(selector, callback) {
            const element = document.querySelector(selector);
            if (element) {
                callback();
            } else {
                const observer = new MutationObserver((mutations, obs) => {
                    const found = document.querySelector(selector);
                    if (found) {
                        callback();
                        obs.disconnect();
                    }
                });
                // Observe the body (or html) for changes
                observer.observe(document.body || document.documentElement, { childList: true, subtree: true });
            }
        }

        injectStyles() {
             // Using GM_addStyle directly with a template literal
            GM_addStyle(`
                #linuxdo-enhancer-sidebar {
                    position: fixed;
                    top: 0;
                    left: 0; /* Visible by default */
                    bottom: 0;
                    width: ${this.config.SIDEBAR_WIDTH};
                    background-color: #1e1e1e; /* Deeper dark background */
                    color: #e0e0e0; /* Light text */
                    z-index: 10000; /* High z-index to be on top */
                    box-shadow: 2px 0 15px rgba(0, 0, 0, 0.6);
                    display: flex;
                    flex-direction: column;
                    transition: left 0.3s ease; /* Smooth toggle animation */
                    overflow: hidden; /* Hide sidebar scrollbar */
                    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
                    font-size: 13px; /* Slightly smaller font */
                }

                #linuxdo-enhancer-sidebar.hidden {
                    left: -${this.config.SIDEBAR_WIDTH}; /* Hide by moving off screen */
                }

                #linuxdo-enhancer-toggle-button {
                    position: fixed;
                    top: 10px;
                    left: ${this.config.SIDEBAR_WIDTH}; /* Positioned just outside the sidebar */
                    z-index: 10001; /* Higher than sidebar */
                    background-color: #1e1e1e;
                    color: #64b5f6; /* Accent color */
                    border: 1px solid #444;
                    border-left: none;
                    padding: 5px 8px; /* Smaller padding */
                    cursor: pointer;
                    border-top-right-radius: 5px;
                    border-bottom-right-radius: 5px;
                    box-shadow: 2px 0 5px rgba(0, 0, 0, 0.3);
                    transition: left 0.3s ease, background-color 0.2s ease; /* Match sidebar transition */
                    font-size: 14px; /* Slightly larger icon */
                    line-height: 1; /* Center text */
                    text-align: center;
                }

                 #linuxdo-enhancer-sidebar.hidden + #linuxdo-enhancer-toggle-button {
                    left: 0; /* Move button to left edge when sidebar is hidden */
                 }
                 #linuxdo-enhancer-toggle-button:hover {
                     background-color: #333;
                 }


                #linuxdo-enhancer-sidebar h2 {
                    color: #34c759; /* Accent color */
                    margin: 10px 15px 5px;
                    padding-bottom: 5px;
                    border-bottom: 1px solid #3a3a3a;
                    font-size: 1.2em;
                    flex-shrink: 0;
                    display: flex;
                    justify-content: space-between;
                    align-items: center;
                }

                .linuxdo-enhancer-count-badge {
                    background-color: #444;
                    color: #fff;
                    border-radius: 10px;
                    padding: 2px 8px;
                    font-size: 0.8em;
                }

                /* Search filter removed */
                /*
                #linuxdo-enhancer-filter-controls {
                    padding: 10px 15px;
                    background: #2a2a2a;
                    flex-shrink: 0;
                }
                #linuxdo-enhancer-search {
                    width: 100%; padding: 8px; border: 1px solid #444; border-radius: 4px;
                    background-color: #3a3a3a; color: #dcdcdc; box-sizing: border-box; font-size: 1em;
                }
                #linuxdo-enhancer-search::placeholder { color: #aaa; }
                #linuxdo-enhancer-search:focus { outline: none; border-color: #64b5f6; box-shadow: 0 0 5px rgba(100, 181, 246, 0.5); }
                */


                #linuxdo-enhancer-posts {
                    flex-grow: 1;
                    overflow-y: scroll; /* Use native scroll but hide scrollbar */
                    -ms-overflow-style: none;
                    scrollbar-width: none;
                    padding: 0 15px;
                    mask-image: linear-gradient(to bottom, transparent 0%, black 5%, black 95%, transparent 100%);
                    -webkit-mask-image: linear-gradient(to bottom, transparent 0%, black 5%, black 95%, transparent 100%);
                     scroll-behavior: smooth; /* Smooth scrolling */
                }

                #linuxdo-enhancer-posts::-webkit-scrollbar {
                    display: none;
                }

                .linuxdo-enhancer-post-item {
                    background-color: #2a2a2a; /* Card background */
                    padding: 12px;
                    margin-bottom: 10px;
                    border-radius: 6px;
                    border-left: 3px solid #64b5f6; /* Accent color */
                    opacity: 0.95;
                    transition: all 0.2s ease;
                    display: flex;
                    flex-direction: column;
                    overflow: hidden;
                    position: relative; /* For meta positioning */
                     box-shadow: 0 2px 5px rgba(0,0,0,0.3);
                }

                .linuxdo-enhancer-post-item:hover {
                    opacity: 1;
                    background-color: #3a3a3a; /* Darker on hover */
                    transform: translateY(-2px); /* Lift effect */
                    box-shadow: 0 4px 8px rgba(0,0,0,0.4);
                }

                 .linuxdo-enhancer-post-header {
                     display: flex;
                     align-items: center;
                     margin-bottom: 8px;
                 }

                 .linuxdo-enhancer-avatar {
                     width: 24px;
                     height: 24px;
                     border-radius: 50%;
                     margin-right: 8px;
                     flex-shrink: 0;
                      background-color: #555; /* Placeholder background */
                      object-fit: cover; /* Prevent stretching */
                 }

                 .linuxdo-enhancer-user-info {
                     flex-grow: 1;
                 }

                 .linuxdo-enhancer-username {
                     font-weight: bold;
                     color: #fff;
                     font-size: 1em;
                     white-space: nowrap; /* Prevent wrapping */
                     overflow: hidden;
                     text-overflow: ellipsis;
                 }
                 .linuxdo-enhancer-user-title {
                     font-size: 0.7em;
                     color: #aaa;
                     white-space: nowrap;
                     overflow: hidden;
                     text-overflow: ellipsis;
                 }


                .linuxdo-enhancer-topic-title a {
                    color: #e0e0e0;
                    text-decoration: none;
                    font-weight: bold;
                    font-size: 1.1em;
                    display: block;
                    margin-bottom: 5px;
                    overflow: hidden;
                    text-overflow: ellipsis;
                    white-space: nowrap;
                }

                .linuxdo-enhancer-topic-title a:hover {
                    color: #ffffff;
                    text-decoration: underline;
                }

                 .linuxdo-enhancer-excerpt {
                     font-size: 0.9em;
                     color: #bbb;
                     margin-bottom: 8px;
                     display: -webkit-box;
                     -webkit-line-clamp: 2; /* Limit excerpt to 2 lines */
                     -webkit-box-orient: vertical;
                     overflow: hidden;
                     text-overflow: ellipsis;
                     line-height: 1.4;
                 }


                .linuxdo-enhancer-post-meta {
                    font-size: 0.8em;
                    color: #aaa;
                    display: flex;
                    flex-wrap: wrap;
                    justify-content: space-between; /* Spread items */
                    margin-top: auto; /* Push to bottom */
                    padding-top: 5px;
                    border-top: 1px solid #333; /* Separator line */
                }

                .linuxdo-enhancer-post-meta span {
                    margin-right: 10px;
                    display: flex;
                    align-items: center;
                }
                 .linuxdo-enhancer-post-meta span:last-child {
                     margin-right: 0;
                 }

                 .linuxdo-enhancer-meta-icon {
                     margin-right: 3px;
                     width: 12px;
                     height: 12px;
                     fill: currentColor; /* Use parent text color */
                     vertical-align: middle;
                     flex-shrink: 0; /* Prevent icon from shrinking */
                 }
                  /* Adjust specific icons if needed */
                  .linuxdo-enhancer-meta-icon.clock { color: #ffeb3b; } /* Yellow for time */
                  .linuxdo-enhancer-meta-icon.topic { color: #00b0ff; } /* Blue for topic */
                  .linuxdo-enhancer-meta-icon.floor { color: #00e676; } /* Green for floor */
                  .linuxdo-enhancer-meta-icon.heart { color: #ff5252; } /* Red for likes */
                  .linuxdo-enhancer-meta-icon.eye { color: #9c27b0; } /* Purple for views */
                  .linuxdo-enhancer-meta-icon.reply { color: #ff9800; } /* Orange for replies */


                #linuxdo-enhancer-ai-analysis {
                    flex-shrink: 0;
                    max-height: 45%; /* Slightly more space for AI */
                    overflow-y: auto;
                    padding: 0 15px 15px;
                    font-size: 0.9em;
                    line-height: 1.5;
                    scrollbar-width: thin;
                    scrollbar-color: #555 #2a2a2a;
                }

                #linuxdo-enhancer-ai-analysis::-webkit-scrollbar {
                    width: 6px;
                }

                #linuxdo-enhancer-ai-analysis::-webkit-scrollbar-track {
                    background: #2a2a2a;
                }

                #linuxdo-enhancer-ai-analysis::-webkit-scrollbar-thumb {
                    background-color: #555;
                    border-radius: 3px;
                }

                .linuxdo-enhancer-analysis-item {
                    background-color: #2a2a2a;
                    padding: 12px;
                    margin-bottom: 12px;
                    border-radius: 6px;
                    border-left: 3px solid #34c759;
                    box-shadow: 0 2px 5px rgba(0,0,0,0.3);
                }
                 .linuxdo-enhancer-analysis-item:last-child {
                     margin-bottom: 0;
                 }


                .linuxdo-enhancer-analysis-item h3 {
                    color: #58e27c;
                    margin-top: 0;
                    margin-bottom: 6px;
                    font-size: 1.1em;
                }

                 .linuxdo-enhancer-analysis-item p {
                    margin: 0;
                    color: #dcdcdc;
                    word-break: break-word; /* Prevent long words from overflowing */
                 }

                .linuxdo-enhancer-analysis-item a {
                    color: #64b5f6;
                    text-decoration: underline;
                }
                .linuxdo-enhancer-analysis-item .markdown-body {
                    /* Basic markdown styles within the analysis item */
                    word-break: break-word;
                }
                 .linuxdo-enhancer-analysis-item .markdown-body p { margin-bottom: 0.5em; }
                 .linuxdo-enhancer-analysis-item .markdown-body a { color: #64b5f6; text-decoration: underline; }
                 /* Markdown list styles */
                 .linuxdo-enhancer-analysis-item .markdown-body ul,
                 .linuxdo-enhancer-analysis-item .markdown-body ol {
                     margin: 0.5em 0;
                     padding-left: 1.5em;
                     color: #ccc; /* List item color */
                 }
                 .linuxdo-enhancer-analysis-item .markdown-body li {
                     margin-bottom: 0.3em;
                 }
                  /* Markdown code styles */
                 .linuxdo-enhancer-analysis-item .markdown-body code {
                      font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
                      background-color: rgba(100, 181, 246, 0.1); /* Light blue background */
                      color: #64b5f6; /* Accent color */
                      padding: 0.1em 0.3em;
                      border-radius: 3px;
                      font-size: 0.9em;
                 }


                .linuxdo-enhancer-loading, .linuxdo-enhancer-empty-state {
                    display: flex;
                    flex-direction: column;
                    align-items: center;
                    justify-content: center;
                    padding: 20px;
                    color: #aaa;
                    text-align: center;
                }

                .linuxdo-enhancer-spinner {
                    border: 3px solid rgba(255,255,255,0.3);
                    border-radius: 50%;
                    border-top: 3px solid #64b5f6;
                    width: 20px;
                    height: 20px;
                    animation: linuxdo-enhancer-spin 1s linear infinite;
                    margin-bottom: 10px;
                }

                @keyframes linuxdo-enhancer-spin {
                    0% { transform: rotate(0deg); }
                    100% { transform: rotate(360deg); }
                }

                .linuxdo-enhancer-error {
                    color: #ff5252;
                    padding: 10px;
                    background-color: rgba(255, 82, 82, 0.1);
                    border-radius: 4px;
                    margin: 10px 15px; /* Match sidebar padding */
                    font-size: 0.9em;
                    word-break: break-word;
                }

                #linuxdo-enhancer-footer {
                    font-size: 0.8em;
                    color: #aaa;
                    padding: 10px 15px; /* Match content padding */
                    flex-shrink: 0;
                    border-top: 1px solid #3a3a3a;
                    display: flex;
                    justify-content: space-between;
                    align-items: center;
                }

                #linuxdo-enhancer-footer-buttons {
                    display: flex;
                    gap: 8px; /* Increased gap */
                    align-items: center;
                }

                 #linuxdo-enhancer-footer-buttons svg {
                     width: 14px;
                     height: 14px;
                     /* fill: #aaa; <-- Removed, use currentColor */
                     vertical-align: middle;
                     flex-shrink: 0;
                 }


                #linuxdo-enhancer-footer button {
                    background: none; /* No background */
                    border: none; /* No border */
                    color: #aaa;
                    padding: 0; /* No padding */
                    cursor: pointer;
                    font-size: 1em; /* Match parent font size */
                    transition: color 0.2s ease;
                    display: flex; /* Align icon and text */
                    align-items: center;
                    gap: 3px; /* Space between icon and text */
                }

                #linuxdo-enhancer-footer button:hover {
                    color: #dcdcdc;
                }
                #linuxdo-enhancer-footer button:hover svg {
                    fill: #dcdcdc;
                }


                #linuxdo-enhancer-footer button.active {
                    color: #34c759; /* Active color */
                }
                 #linuxdo-enhancer-footer button.active svg {
                     fill: #34c759;
                 }


                #linuxdo-enhancer-update-time {
                    font-size: 0.9em;
                    color: #777;
                }

                /* Adjust body padding when sidebar is visible */
                body {
                    padding-left: ${this.config.SIDEBAR_WIDTH} !important;
                    transition: padding-left 0.3s ease;
                }

                body.linuxdo-enhancer-sidebar-hidden {
                    padding-left: 0 !important;
                }

                 /* Ensure main Discourse content adjusts */
                #main-outlet {
                     width: 100% !important;
                     box-sizing: border-box !important;
                }
                /* If header is fixed, adjust its position */
                 .d-header-wrap {
                     left: ${this.config.SIDEBAR_WIDTH} !important;
                     width: calc(100% - ${this.config.SIDEBAR_WIDTH}) !important;
                     transition: left 0.3s ease, width 0.3s ease;
                 }
                 body.linuxdo-enhancer-sidebar-hidden .d-header-wrap {
                     left: 0 !important;
                     width: 100% !important;
                 }


                /* Config Modal */
                #linuxdo-enhancer-config-modal {
                    display: none;
                    position: fixed;
                    z-index: 20000;
                    left: 0;
                    top: 0;
                    width: 100%;
                    height: 100%;
                    overflow: auto;
                    background-color: rgba(0,0,0,0.8);
                    justify-content: center;
                    align-items: center;
                }

                #linuxdo-enhancer-config-modal-content {
                    background-color: #2a2a2a;
                    padding: 30px;
                    border: 1px solid #888;
                    width: 90%;
                    max-width: 400px;
                    border-radius: 10px;
                    text-align: center;
                    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.7);
                     color: #dcdcdc;
                }

                #linuxdo-enhancer-config-modal-content h2 {
                    color: #64b5f6;
                    margin: 0 0 20px 0; /* Adjusted margin */
                    font-size: 1.5em;
                    border-bottom: none; /* Remove h2 border */
                }

                #linuxdo-enhancer-config-modal-content p {
                    margin-bottom: 15px;
                    color: #bbb;
                }

                 #linuxdo-enhancer-api-url-select-container {
                     margin-bottom: 20px;
                 }

                 #linuxdo-enhancer-api-url-select-container label {
                     color: #ccc;
                     margin-right: 5px;
                 }

                 #linuxdo-enhancer-api-url-select {
                    padding: 8px;
                    border: 1px solid #555;
                    border-radius: 4px;
                    background-color: #333;
                    color: #dcdcdc;
                    font-size: 1em;
                 }

                .linuxdo-enhancer-config-section {
                    margin-bottom: 20px;
                    padding: 15px;
                    border: 1px solid #444;
                    border-radius: 8px;
                    background-color: #333;
                    text-align: left; /* Align text left within sections */
                }
                 .linuxdo-enhancer-config-section h3 {
                     color: #34c759;
                     margin-top: 0;
                     margin-bottom: 10px;
                     font-size: 1.2em;
                     border-bottom: 1px solid #444;
                     padding-bottom: 5px;
                 }
                 .linuxdo-enhancer-config-section label {
                     display: block; /* Label on its own line */
                     margin-bottom: 5px;
                     color: #ccc;
                 }
                 .linuxdo-enhancer-config-section input[type="number"] {
                     width: calc(100% - 22px);
                     padding: 8px;
                     border: 1px solid #555;
                     border-radius: 4px;
                     background-color: #2a2a2a;
                     color: #dcdcdc;
                     box-sizing: border-box;
                     font-size: 1em;
                     margin-bottom: 10px;
                 }
                 .linuxdo-enhancer-config-section input[type="number"]:focus {
                     outline: none;
                     border-color: #64b5f6;
                     box-shadow: 0 0 5px rgba(100, 181, 246, 0.5);
                 }
                 .linuxdo-enhancer-config-section input[type="number"]::-webkit-outer-spin-button,
                 .linuxdo-enhancer-config-section input[type="number"]::-webkit-inner-spin-button {
                     -webkit-appearance: none;
                     margin: 0;
                 }


                #linuxdo-enhancer-config-modal-content input[type="password"],
                #linuxdo-enhancer-config-modal-content input[type="text"] { /* Allow text for visibility option */
                    width: calc(100% - 22px);
                    padding: 10px;
                    margin-bottom: 20px;
                    border: 1px solid #555;
                    border-radius: 5px;
                    background-color: #333;
                    color: #dcdcdc;
                    font-size: 1em;
                    box-sizing: border-box;
                }

                #linuxdo-enhancer-config-modal-content button {
                    background-color: #34c759;
                    color: white;
                    padding: 10px 20px;
                    border: none;
                    border-radius: 5px;
                    cursor: pointer;
                    font-size: 1.1em;
                    transition: background-color 0.3s ease;
                     /* Override button style from footer */
                     display: inline-block;
                     gap: 0;
                }

                #linuxdo-enhancer-config-modal-content button:hover:not(:disabled) {
                    background-color: #58e27c;
                }

                #linuxdo-enhancer-config-modal-content button:disabled {
                    background-color: #555;
                    cursor: not-allowed;
                }

                #linuxdo-enhancer-config-modal-content .warning {
                    font-size: 0.9em;
                    color: #ffeb3b;
                    margin-top: 20px;
                }

                /* Responsive */
                @media (max-width: 768px) {
                    #linuxdo-enhancer-sidebar {
                        width: 260px; /* Slightly smaller on mobile */
                    }

                    body {
                        padding-left: 260px !important;
                    }

                    #linuxdo-enhancer-toggle-button {
                        left: 260px;
                    }
                     .d-header-wrap {
                         left: 260px !important;
                         width: calc(100% - 260px) !important;
                     }
                     body.linuxdo-enhancer-sidebar-hidden .d-header-wrap {
                         left: 0 !important;
                         width: 100% !important;
                     }
                     #linuxdo-enhancer-footer {
                         flex-direction: column;
                         gap: 8px;
                     }
                     #linuxdo-enhancer-footer-buttons {
                         justify-content: center;
                     }
                     .linuxdo-enhancer-config-section input[type="number"] {
                          width: calc(100% - 18px); /* Adjust for mobile padding */
                     }


                }
            `);
        }

        waitForBody(callback) {
            if (document.body) {
                callback();
            } else {
                const observer = new MutationObserver((mutations, obs) => {
                    if (document.body) {
                        callback();
                        obs.disconnect();
                    }
                });
                observer.observe(document.documentElement, { childList: true, subtree: true });
            }
        }

        injectUI() {
            const sidebarHTML = `
                <div id="linuxdo-enhancer-sidebar">
                    <h2>最新帖子 <span id="linuxdo-enhancer-post-count" class="linuxdo-enhancer-count-badge">0</span></h2>
                    <div id="linuxdo-enhancer-posts">
                        <div class="linuxdo-enhancer-loading">
                            <div class="linuxdo-enhancer-spinner"></div>
                            <span>加载中...</span>
                        </div>
                    </div>
                    <h2>AI 智能分析 <span id="linuxdo-enhancer-ai-status" class="linuxdo-enhancer-count-badge">等待</span></h2>
                    <div id="linuxdo-enhancer-ai-analysis">
                         <div class="linuxdo-enhancer-loading">
                            <div class="linuxdo-enhancer-spinner"></div>
                            <span>等待获取最新帖子并进行分析...</span>
                         </div>
                    </div>
                    <div id="linuxdo-enhancer-footer">
                        <div id="linuxdo-enhancer-update-time">最后更新: --</div>
                        <div id="linuxdo-enhancer-footer-buttons">
                            <button id="linuxdo-enhancer-settings-button" title="设置 Gemini API 密钥和地址">
                                <svg class="linuxdo-enhancer-meta-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12.0007 2C16.4198 2 20.0007 5.58079 20.0007 10C20.0007 14.4192 16.4198 18 12.0007 18C7.58155 18 4.00073 14.4192 4.00073 10C4.00073 5.58079 7.58155 2 12.0007 2ZM12.0007 20C17.5235 20 22.0007 15.5228 22.0007 10C22.0007 4.47715 17.5235 0 12.0007 0C6.47788 0 2.00073 4.47715 2.00073 10C2.00073 15.5228 6.47788 20 12.0007 20ZM12.0007 13C10.3439 13 9.00073 11.6569 9.00073 10C9.00073 8.34315 10.3439 7 12.0007 7C13.6575 7 15.0007 8.34315 15.0007 10C15.0007 11.6569 13.6575 13 12.0007 13ZM12.0007 15C14.7615 15 17.0007 12.7614 17.0007 10C17.0007 7.23858 14.7615 5 12.0007 5C9.23906 5 7.00073 7.23858 7.00073 10C7.00073 12.7614 9.23906 15 12.0007 15ZM12.0007 21C9.49511 21 7.20048 20.1717 5.51847 18.8092L6.93268 17.395C8.16129 18.3754 10.0007 19 12.0007 19C14.0007 19 15.8393 18.3754 17.0679 17.395L18.4821 18.8092C16.7994 20.1717 14.5048 21 12.0007 21ZM2.41452 18.8092C0.732504 17.1717 -0.000734264 14.5048 -0.000734264 12C-0.000734264 9.49544 0.827626 7.20081 2.19014 5.5188L3.60435 6.93301C2.62391 8.16162 2.00073 10 2.00073 12C2.00073 14 2.62391 15.8386 3.60435 17.0672L2.19014 18.4814C2.26299 18.5782 2.33813 18.673 2.41452 18.8092Z"/></svg>
                                设置
                            </button>
                            <button id="linuxdo-enhancer-pause-scroll" title="暂停或继续帖子列表滚动">
                                <svg class="linuxdo-enhancer-meta-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M14 19V5H18V19H14ZM6 19V5H10V19H6Z"></path></svg>
                                暂停
                            </button>
                        </div>
                    </div>
                </div>
                <button id="linuxdo-enhancer-toggle-button" title="隐藏/显示侧边栏">≡</button>
                <div id="linuxdo-enhancer-config-modal">
                    <div id="linuxdo-enhancer-config-modal-content">
                        <h2>配置 Gemini API 及脚本设置</h2>
                        <p>输入您的 Google Gemini API 密钥并选择 API 地址：</p>

                         <div class="linuxdo-enhancer-config-section">
                             <h3>API 设置</h3>
                             <div id="linuxdo-enhancer-api-url-select-container">
                                <label for="linuxdo-enhancer-api-url-select">API 地址:</label>
                                <select id="linuxdo-enhancer-api-url-select">
                                    <option value="https://generativelanguage.googleapis.com/v1beta/models/">Google 官方 API</option>
                                    <option value="https://g.shatang.me/v1beta/models/">g.shatang.me (社区提供)</option>
                                    <!-- Add other options here if needed -->
                                </select>
                             </div>
                             <label for="linuxdo-enhancer-gemini-api-key-input">API 密钥:</label>
                            <input type="password" id="linuxdo-enhancer-gemini-api-key-input" placeholder="粘贴您的 Gemini API 密钥">
                             <p class="warning">注意：密钥和选择的API地址将保存在您的浏览器本地（油猴存储），请勿分享截图。</p>
                            <p class="warning">建议使用专用API密钥并定期轮换。</p>
                         </div>

                        <div class="linuxdo-enhancer-config-section">
                            <h3>刷新频率 (秒)</h3>
                             <label for="linuxdo-enhancer-fetch-interval-input">帖子列表刷新:</label>
                             <input type="number" id="linuxdo-enhancer-fetch-interval-input" min="10" value="30">
                             <label for="linuxdo-enhancer-analyze-interval-input">AI 分析刷新:</label>
                             <input type="number" id="linuxdo-enhancer-analyze-interval-input" min="60" value="120">
                         </div>


                        <button id="linuxdo-enhancer-save-config-button" disabled>保存并应用</button>

                    </div>
                </div>
            `;

            document.body.insertAdjacentHTML('beforeend', sidebarHTML);
        }

        initElements() {
            this.domElements = {
                sidebar: document.getElementById('linuxdo-enhancer-sidebar'),
                toggleButton: document.getElementById('linuxdo-enhancer-toggle-button'),
                postList: document.getElementById('linuxdo-enhancer-posts'),
                aiAnalysisDiv: document.getElementById('linuxdo-enhancer-ai-analysis'),
                // Removed search input: searchInput: document.getElementById('linuxdo-enhancer-search'),
                postCount: document.getElementById('linuxdo-enhancer-post-count'),
                aiStatus: document.getElementById('linuxdo-enhancer-ai-status'),
                updateTime: document.getElementById('linuxdo-enhancer-update-time'),
                settingsButton: document.getElementById('linuxdo-enhancer-settings-button'),
                pauseScrollButton: document.getElementById('linuxdo-enhancer-pause-scroll'),
                configModal: document.getElementById('linuxdo-enhancer-config-modal'),
                apiKeyInput: document.getElementById('linuxdo-enhancer-gemini-api-key-input'),
                saveConfigButton: document.getElementById('linuxdo-enhancer-save-config-button'),
                apiUrlSelect: document.getElementById('linuxdo-enhancer-api-url-select'), // Get select element
                fetchIntervalInput: document.getElementById('linuxdo-enhancer-fetch-interval-input'), // Get new input
                analyzeIntervalInput: document.getElementById('linuxdo-enhancer-analyze-interval-input') // Get new input
            };
             // Check if all critical elements were found
             const criticalElements = [
                 'sidebar', 'toggleButton', 'postList', 'aiAnalysisDiv',
                 'postCount', 'aiStatus', 'updateTime',
                 'settingsButton', 'pauseScrollButton', 'configModal',
                 'apiKeyInput', 'saveConfigButton', 'apiUrlSelect',
                 'fetchIntervalInput', 'analyzeIntervalInput' // Added new elements
             ];
             for (const key of criticalElements) {
                 if (!this.domElements[key]) {
                     console.error(`LinuxDoEnhancer Error: Critical DOM element "${key}" not found! Script may not function correctly.`);
                     // Potentially display a persistent error message on the page if sidebar exists
                     if(this.domElements.sidebar) {
                          this.domElements.sidebar.innerHTML = `<div class="linuxdo-enhancer-error">脚本初始化失败：找不到关键UI元素 (${key})。请尝试刷新页面或检查脚本是否正确安装。</div>`;
                     } else {
                          alert(`LinuxDoEnhancer Error: Critical DOM element "${key}" not found! Script initialization failed.`);
                     }
                     // Stop initialization process
                     throw new Error(`Missing critical DOM element: ${key}`);
                 }
             }

             // Initial state for pause button text/icon
             this.domElements.pauseScrollButton.innerHTML = '<svg class="linuxdo-enhancer-meta-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M14 19V5H18V19H14ZM6 19V5H10V19H6Z"></path></svg> 暂停'; // Pause icon
        }

        setupEventListeners() {
            this.domElements.toggleButton.addEventListener('click', () => this.toggleSidebar());
            this.domElements.pauseScrollButton.addEventListener('click', () => this.toggleScroll());
            this.domElements.settingsButton.addEventListener('click', () => this.showConfigModal());

            // Enable save button if API key and intervals are valid
            const checkSaveButtonState = () => {
                 const key = this.domElements.apiKeyInput.value.trim();
                 const fetchInterval = parseInt(this.domElements.fetchIntervalInput.value, 10);
                 const analyzeInterval = parseInt(this.domElements.analyzeIntervalInput.value, 10);
                 this.domElements.saveConfigButton.disabled = !(key && fetchInterval >= 10 && analyzeInterval >= 60);
            };

            this.domElements.apiKeyInput.addEventListener('input', checkSaveButtonState);
            this.domElements.fetchIntervalInput.addEventListener('input', checkSaveButtonState);
            this.domElements.analyzeIntervalInput.addEventListener('input', checkSaveButtonState);
            this.domElements.saveConfigButton.addEventListener('click', () => this.handleSaveConfig());

            // Pause/resume scrolling on post list hover
            this.domElements.postList.addEventListener('mouseenter', () => this.stopScrolling());
            this.domElements.postList.addEventListener('mouseleave', () => {
                if (!this.state.isScrollingPaused) {
                    this.startScrolling();
                }
            });
        }

        loadConfigAndStart() {
             // Load API Key
            const savedApiKey = GM_getValue(this.config.API_KEY_STORAGE_KEY);

             // Load API URL preference
             const savedApiUrl = GM_getValue(this.config.API_URL_STORAGE_KEY);
             if (savedApiUrl) {
                 this.config.GEMINI_API_BASE_URL = savedApiUrl;
             } // Default is already set in constructor

             // Load intervals preference
             const savedFetchInterval = GM_getValue(this.config.FETCH_INTERVAL_STORAGE_KEY);
             const savedAnalyzeInterval = GM_getValue(this.config.ANALYZE_INTERVAL_STORAGE_KEY);

             // Use saved value if valid, otherwise use default
             this.config.POST_FETCH_INTERVAL = savedFetchInterval && savedFetchInterval >= 10 * 1000 ? savedFetchInterval : this.config.DEFAULT_POST_FETCH_INTERVAL;
             this.config.AI_ANALYZE_INTERVAL = savedAnalyzeInterval && savedAnalyzeInterval >= 60 * 1000 ? savedAnalyzeInterval : this.config.DEFAULT_AI_ANALYZE_INTERVAL;

            if (savedApiKey) {
                console.log("API key found. Starting fetch and analysis with loaded config.");
                this.startFetchingAndAnalyzing();
            } else {
                console.log("API key not found. Showing config modal.");
                this.showConfigModal();
            }
        }

        toggleSidebar() {
            this.state.isSidebarVisible = !this.state.isSidebarVisible;
            this.domElements.sidebar.classList.toggle('hidden', !this.state.isSidebarVisible);
            document.body.classList.toggle('linuxdo-enhancer-sidebar-hidden', !this.state.isSidebarVisible);
             // Adjust header position immediately on toggle
             const header = document.querySelector('.d-header-wrap');
             if (header) {
                 if (this.state.isSidebarVisible) {
                      header.style.left = this.config.SIDEBAR_WIDTH;
                      header.style.width = `calc(100% - ${this.config.SIDEBAR_WIDTH})`;
                      // When showing, restart intervals if not paused
                      if (!this.state.isScrollingPaused) {
                           this.startFetchingAndAnalyzing();
                      } else {
                           // If paused, only start scrolling
                           this.startScrolling();
                      }
                 } else {
                      header.style.left = '0';
                      header.style.width = '100%';
                      // When hiding, stop all intervals
                      this.stopFetchingAndAnalyzing();
                 }
             }
        }

        toggleScroll() {
            this.state.isScrollingPaused = !this.state.isScrollingPaused;
            this.domElements.pauseScrollButton.classList.toggle('active', this.state.isScrollingPaused);

            if (this.state.isScrollingPaused) {
                 this.domElements.pauseScrollButton.innerHTML = '<svg class="linuxdo-enhancer-meta-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M6 4V20H10V4H6ZM14 4V20H18V4H14Z"/></svg> 继续'; // Play icon (reused pause icon path)
                 this.stopScrolling();
            } else {
                 this.domElements.pauseScrollButton.innerHTML = '<svg class="linuxdo-enhancer-meta-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M14 19V5H18V19H14ZM6 19V5H10V19H6Z"></path></svg> 暂停'; // Pause icon
                 this.startScrolling();
            }
        }

        startScrolling() {
            // Only start scrolling if sidebar is visible and scrolling is not paused
            if (this.state.scrollIntervalId || this.state.isScrollingPaused || !this.state.isSidebarVisible) return;

            this.state.scrollIntervalId = setInterval(() => {
                const list = this.domElements.postList;
                if (!list || list.scrollHeight <= list.clientHeight) { // Defensive check & stop if no overflow
                    this.stopScrolling();
                    return;
                }
                const { scrollTop, scrollHeight, clientHeight } = list;

                // If we are at the bottom or near bottom, jump back to top
                if (scrollTop + clientHeight >= scrollHeight - this.config.SCROLL_AMOUNT) {
                    // Small delay before jumping to top
                    setTimeout(() => { list.scrollTop = 0; }, 500);
                } else {
                    list.scrollTop += this.config.SCROLL_AMOUNT;
                }
            }, this.config.SCROLL_INTERVAL);
        }

        stopScrolling() {
            if (this.state.scrollIntervalId) {
                clearInterval(this.state.scrollIntervalId);
                this.state.scrollIntervalId = null;
            }
        }


        startFetchingAndAnalyzing() {
             // Only start if sidebar is visible
             if (!this.state.isSidebarVisible) {
                 console.log("Sidebar is hidden, skipping start of intervals.");
                 return;
             }

            // Clear existing intervals before starting new ones
            this.stopFetchingAndAnalyzing();

            // Initial fetch
            this.fetchPosts().then(() => {
                // Start initial analysis a bit after fetch completes and posts are rendered
                 setTimeout(() => {
                     if (this.state.rawPostData.length > 0) {
                         this.analyzePostsWithAI();
                     } else {
                          console.log("Initial fetch yielded no posts. Skipping first analysis.");
                          if(this.domElements.aiAnalysisDiv) {
                             this.domElements.aiAnalysisDiv.innerHTML = '<div class="linuxdo-enhancer-analysis-item"><p>未能加载帖子，等待下一次尝试...</p></div>';
                          }
                          if(this.domElements.aiStatus) {
                             this.domElements.aiStatus.textContent = '等待';
                          }
                     }
                 }, 2000); // Wait 2 seconds after fetch attempts
            });

            // Set up intervals using configured frequencies
            this.intervals.fetchInterval = setInterval(() => this.fetchPosts(), this.config.POST_FETCH_INTERVAL);
            this.intervals.analyzeInterval = setInterval(() => {
                 if (this.state.rawPostData.length > 0) {
                      this.analyzePostsWithAI();
                 } else {
                      console.log("No posts available for analysis this cycle.");
                       if(this.domElements.aiAnalysisDiv) {
                          this.domElements.aiAnalysisDiv.innerHTML = '<div class="linuxdo-enhancer-analysis-item"><p>等待加载最新帖子进行分析...</p></div>';
                       }
                       if(this.domElements.aiStatus) {
                         this.domElements.aiStatus.textContent = '等待';
                       }
                 }
            }, this.config.AI_ANALYZE_INTERVAL);

            console.log(`Enhancer started: fetching every ${this.config.POST_FETCH_INTERVAL/1000}s, analyzing every ${this.config.AI_ANALYZE_INTERVAL/1000}s.`);
             // Adjust header position on start (redundant if called by toggle, but safe)
             const header = document.querySelector('.d-header-wrap');
             if (header) {
                  header.style.left = this.config.SIDEBAR_WIDTH;
                  header.style.width = `calc(100% - ${this.config.SIDEBAR_WIDTH})`;
             }
             // Ensure scrolling starts if not paused
             this.startScrolling();
        }

        stopFetchingAndAnalyzing() {
            if (this.intervals.fetchInterval) {
                clearInterval(this.intervals.fetchInterval);
                this.intervals.fetchInterval = null;
            }
            if (this.intervals.analyzeInterval) {
                clearInterval(this.intervals.analyzeInterval);
                this.intervals.analyzeInterval = null;
            }
             // Stop scrolling when intervals are stopped (except if explicitly paused?)
             // Decided to let toggleScroll handle scroll pausing state separately
             // this.stopScrolling(); // Do not stop scrolling here, let toggle handle it

            console.log("Enhancer intervals cleared.");
             // Reset header position (redundant if called by toggle, but safe)
             const header = document.querySelector('.d-header-wrap');
             if (header && !this.state.isSidebarVisible) { // Only reset if sidebar is actually hidden
                 header.style.left = '0';
                 header.style.width = '100%';
             }
        }

        async fetchPosts() {
            if (this.state.isFetchingPosts) {
                console.log("Fetch in progress, skipping.");
                return;
            }
             // Only fetch if sidebar is visible
             if (!this.state.isSidebarVisible) {
                  console.log("Sidebar is hidden, skipping fetch.");
                  return;
             }

            this.state.isFetchingPosts = true;
            console.log('Fetching posts...');

            const postListElement = this.domElements.postList;
            const updateTimeElement = this.domElements.updateTime;


            try {
                // Only show full loading state if list is currently empty or has error
                if (this.state.rawPostData.length === 0 || (postListElement && postListElement.querySelector('.linuxdo-enhancer-error'))) {
                     this.showLoading(postListElement);
                } else {
                    console.log("Fetching new posts...");
                }

                if(this.domElements.aiStatus) this.domElements.aiStatus.textContent = '更新中';

                const response = await fetch(this.config.POSTS_URL);
                if (!response.ok) {
                     const errorText = await response.text();
                    throw new Error(`HTTP error! status: ${response.status}, body: ${errorText.substring(0, 100)}...`);
                }

                const data = await response.json();

                // Check if data format is as expected for latest_posts
                if (Array.isArray(data.latest_posts)) {
                    const newPosts = data.latest_posts.map(post => ({
                        id: post.id, // Post ID
                        topic_id: post.topic_id,
                        topic_slug: post.topic_slug,
                        topic_title: post.topic_title,
                        fancy_title: post.topic_html_title || post.topic_title, // Use HTML title which might have emojis
                        post_number: post.post_number, // Floor number
                        created_at: post.created_at, // UTC time
                        updated_at: post.updated_at, // UTC time
                        views: post.views, // Views for the TOPIC
                        posts_count: post.posts_count, // Total posts in the TOPIC
                        author: post.username || '未知', // Use username directly from JSON
                        display_author_name: post.display_username, // Store display_username separately
                         // --- Simplified Avatar Path ---
                        author_avatar_template: post.avatar_template, // Store the template directly
                         // --- End Simplified Avatar Path ---
                        user_title: post.user_title, // User's custom title
                        link: `https://linux.do/t/${post.topic_slug}/${post.topic_id}${post.post_number ? `/${post.post_number}` : ''}`, // Link directly to the post, handle first post edge case
                        raw: post.raw, // Raw markdown
                        cooked: post.cooked, // Cooked HTML (can contain images, links etc.)
                        excerpt: post.excerpt, // Short text excerpt
                        post_likes: post.reaction_users_count || 0 // Likes for THIS POST - Use this for per-post likes
                    }));

                    // Simple check if posts have changed significantly (first few IDs)
                    const currentTopIds = this.state.rawPostData.slice(0, 5).map(p => p.id).join(',');
                    const newTopIds = newPosts.slice(0, 5).map(p => p.id).join(',');

                    if (!this.state.rawPostData.length || newTopIds !== currentTopIds) {
                        console.log('New posts received. Updating display.');
                        this.state.rawPostData = newPosts;
                         // No filtering needed after removing search
                         this.state.filteredPostData = [...this.state.rawPostData];
                         this.renderPosts();
                    } else {
                        console.log('No significant change in top posts.');
                         // Ensure filteredData is still a copy of rawData
                        this.state.filteredPostData = [...this.state.rawPostData];
                        // Re-render is needed even if no new posts arrived if there are existing posts
                         if(this.state.rawPostData.length > 0) {
                             this.renderPosts(); // Render existing data
                         } else {
                              // If no raw data, ensure empty state is rendered
                             this.renderPosts();
                         }
                    }
                    this.updatePostCount();
                    this.updateLastUpdateTime();

                } else {
                     console.error('Unexpected data format from posts.json:', data);
                     // Show error only if we couldn't process the data
                     if(postListElement) this.showError(postListElement, '获取的帖子数据格式异常！');
                }
            } catch (error) {
                console.error('Error fetching posts:', error);
                // Show error only if fetch failed and no previous data exists
                 if (this.state.rawPostData.length === 0) {
                    if(postListElement) this.showError(postListElement, `加载帖子失败: ${error.message}`);
                 } else {
                     // If we have old data, maybe just log the error subtly and indicate connection issue
                     console.warn("Failed to fetch new posts, displaying old data.", error);
                      if(updateTimeElement) {
                           updateTimeElement.textContent = `最后更新: ${this.state.lastUpdateTime ? this.state.lastUpdateTime.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit', hour12: false }) : '--'} (连接失败)`;
                      }
                 }

            } finally {
                this.state.isFetchingPosts = false;
            }
        }

        renderPosts() {
            // We now render directly from rawPostData as there's no filtering
            const postsToDisplay = this.state.rawPostData.slice(0, this.config.MAX_DISPLAY_POSTS);
            const postListElement = this.domElements.postList;

            if (!postListElement) { // Defensive check
                console.error("Attempted to render posts, but postList element is null.");
                this.stopScrolling(); // Stop scrolling if element is gone
                return;
            }

            postListElement.innerHTML = ''; // Clear current list

            if (postsToDisplay.length === 0) {
                 postListElement.innerHTML = `
                     <div class="linuxdo-enhancer-empty-state">
                         <svg class="linuxdo-enhancer-meta-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="40" height="40" style="color: #aaa;"><path d="M12.893 3.22194L21.586 12L12.893 20.7781L12 19.8851L20.114 11.7711L12 3.65714L12.893 3.22194ZM3.22194 12.893L12 21.586L20.7781 12.893L19.8851 12L11.7711 20.114L3.65714 12L3.22194 12.893Z"/></svg>
                         <p>暂无帖子。</p>
                     </div>
                 `;
                return;
            }

            postsToDisplay.forEach(post => {
                const postElement = document.createElement('div');
                postElement.classList.add('linuxdo-enhancer-post-item');

                const postDate = new Date(post.created_at);
                // Convert UTC to Beijing Time (UTC+8)
                const options = {
                    // year: 'numeric', // Removed year for brevity
                    month: '2-digit', day: '2-digit',
                    hour: '2-digit', minute: '2-digit', // second: '2-digit', // Removed seconds for brevity
                    timeZone: 'Asia/Shanghai', // Beijing Time
                    hour12: false // Use 24-hour format
                };
                const formattedDate = postDate.toLocaleString('zh-CN', options);

                // Use display_author_name if available, fallback to username
                const displayUsername = post.display_author_name && post.display_author_name.trim() !== '' ? post.display_author_name : post.author;
                // --- Simplified Avatar Path Construction ---
                 // Directly use the template if available, replace size
                 const avatarUrl = post.author_avatar_template ? `https://linux.do${post.author_avatar_template.replace('{size}', '40')}` : null;
                 // Fallback avatar if template is null or invalid (Discourse letter avatars are predictable)
                 // Use the post.author for fallback avatar
                 const defaultAvatarUrl = `https://linux.do/letter_avatar/u/${post.author.substring(0,1).toLowerCase()}/40/1.png`;


                postElement.innerHTML = `
                    <div class="linuxdo-enhancer-post-header">
                         <img src="${avatarUrl || defaultAvatarUrl}" class="linuxdo-enhancer-avatar" alt="${displayUsername || post.author}">
                         <div class="linuxdo-enhancer-user-info">
                             <div class="linuxdo-enhancer-username" title="${displayUsername || post.author}">${post.author}</div> <!-- Always display short username with display name as title -->
                             ${post.user_title ? `<div class="linuxdo-enhancer-user-title" title="${post.user_title}">${post.user_title}</div>` : ''}
                         </div>
                    </div>
                    <div class="linuxdo-enhancer-topic-title">
                        <a href="${post.link}" target="_blank" title="${post.fancy_title || post.topic_title}">
                            ${post.fancy_title || post.topic_title}
                        </a>
                    </div>
                    ${post.excerpt ? `<div class="linuxdo-enhancer-excerpt">${post.excerpt.trim()}</div>` : ''}
                    <div class="linuxdo-enhancer-post-meta">
                        <span>
                             <svg class="linuxdo-enhancer-meta-icon clock" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 2C17.52 2 22 6.48 22 12C22 17.52 17.52 22 12 22C6.48 22 2 17.52 2 12C2 6.48 6.48 2 12 2ZM12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4ZM11 12V16H13V12H11ZM11 8H13V10H11V8Z"/></svg>
                             ${formattedDate}
                        </span>
                         <span>
                             <svg class="linuxdo-enhancer-meta-icon floor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2ZM12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20ZM11 12V16H13V12H11ZM11 8H13V10H11V8Z"/></svg>
                             #${post.post_number}
                        </span>
                         <span>
                             <svg class="linuxdo-enhancer-meta-icon heart" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12.001 4.52853C14.345 2.42678 17.9376 2.45879 20.2428 4.58075C22.5491 6.70376 22.6557 10.2053 20.4876 12.4154L11.9999 21.0995L3.51245 12.4154C1.34438 10.2053 1.45096 6.70376 3.75722 4.58075C6.06236 2.45879 9.65502 2.42678 12.001 4.52853Z"></path></svg>
                             ${post.post_likes}
                        </span>
                         ${post.views > 0 ? ` <!-- Only show views if > 0 -->
                         <span>
                             <svg class="linuxdo-enhancer-meta-icon eye" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 4.5C7 4.5 2.73 7.61 1 12C2.73 16.39 7 19.5 12 19.5C17 19.5 21.27 16.39 23 12C21.27 7.61 17 4.5 12 4.5ZM12 17C9.24 17 7 14.76 7 12C7 9.24 9.24 7 12 7C14.76 7 17 9.24 17 12C17 14.76 14.76 17 12 17ZM12 9C10.34 9 9 10.34 9 12C9 13.66 10.34 15 12 15C13.66 15 15 13.66 15 12C15 10.34 13.66 9 12 9Z"/></svg>
                             ${post.views}
                        </span>
                         ` : ''}
                         <span>
                             <svg class="linuxdo-enhancer-meta-icon reply" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M8 11H10V13H8V11ZM14 11H16V13H14V11ZM12 2C17.52 2 22 6.48 22 12C22 14.11 21.34 16.01 20.23 17.55L20.91 20.22L18.22 20.91C16.01 21.66 13.89 22 12 22C6.48 22 2 17.52 2 12C2 6.48 6.48 2 12 2ZM12 20C13.59 20 15.15 19.65 16.53 19.03L18.89 19.63L18.22 16.91C19.32 15.41 20 13.75 20 12C20 7.58 16.42 4 12 4C7.58 4 4 7.58 4 12C4 16.42 7.58 20 12 20ZM7 11H11V15H7V11ZM13 11H17V15H13V11Z"/></svg>
                             ${post.posts_count - 1}
                        </span>
                    </div>
                `;
                postListElement.appendChild(postElement);
            });

            // Reset scroll position only if not filtering (search removed) and not paused
            // Also, only reset if there are posts to scroll
            if (!this.state.isScrollingPaused && postsToDisplay.length > 0) {
                 postListElement.scrollTop = 0;
            }
        }

        updatePostCount() {
            if (this.domElements.postCount) {
                // Count from rawPostData since filtering is removed
                this.domElements.postCount.textContent = this.state.rawPostData.length;
            }
        }

        updateLastUpdateTime() {
             const updateTimeElement = this.domElements.updateTime;
             if (!updateTimeElement) return;

            const now = new Date();
            // Convert to Beijing Time (UTC+8) for display
            const options = {
                 hour: '2-digit', minute: '2-digit', // Removed seconds for brevity
                 timeZone: 'Asia/Shanghai',
                 hour12: false // Use 24-hour format
                 // day and month will be part of the main date if we add it back
            };
            // For "最后更新", maybe just time is sufficient
            const timeString = now.toLocaleTimeString('zh-CN', options);

            // For date, keep month-day
            const dateOptions = {
                 month: '2-digit', day: '2-digit',
                 timeZone: 'Asia/Shanghai',
            }
            const dateString = now.toLocaleDateString('zh-CN', dateOptions).replace(/\//g, '-'); // Format like MM-DD


            updateTimeElement.textContent = `最后更新: ${dateString} ${timeString}`;
            this.state.lastUpdateTime = now; // Store JS Date object
        }


        async analyzePostsWithAI() {
            // Check if AI Analysis section or status badge exist
             const aiAnalysisDiv = this.domElements.aiAnalysisDiv;
             const aiStatus = this.domElements.aiStatus;

             // Only analyze if sidebar is visible and there's data
             if (!this.state.isSidebarVisible || this.state.isAnalyzing || !this.state.rawPostData.length) {
                 console.log("Sidebar hidden, analysis in progress, or no data, skipping analysis.");
                 if (aiStatus) aiStatus.textContent = this.state.isAnalyzing ? '分析中' : (this.state.rawPostData.length > 0 ? aiStatus.textContent : '等待');
                return;
            }
            this.state.isAnalyzing = true;
            console.log('Starting AI analysis...');

            const apiKey = GM_getValue(this.config.API_KEY_STORAGE_KEY);
            if (!apiKey) {
                if (aiAnalysisDiv) this.showError(aiAnalysisDiv, 'Gemini API 密钥未配置');
                if (aiStatus) aiStatus.textContent = '密钥缺失';
                this.state.isAnalyzing = false;
                return;
            }

            try {
                if (aiAnalysisDiv) {
                    this.showLoading(aiAnalysisDiv);
                }
                if (aiStatus) aiStatus.textContent = '分析中';

                // Use recent posts for analysis, ensure data exists
                const postsForPrompt = this.state.rawPostData.slice(0, this.config.AI_ANALYSIS_POST_COUNT);
                 if (postsForPrompt.length === 0) {
                     console.log("No recent posts to send to AI.");
                     if (aiAnalysisDiv) aiAnalysisDiv.innerHTML = '<div class="linuxdo-enhancer-analysis-item"><p>暂无帖子可供分析。</p></div>';
                     if (aiStatus) aiStatus.textContent = '无数据';
                     this.state.isAnalyzing = false;
                     return;
                 }

                // Use the 'author' field which now consistently holds the username from JSON
                const postSummaryText = postsForPrompt.map(post =>
                    `标题: ${post.fancy_title || post.topic_title}\n作者: ${post.author}${post.display_author_name && post.display_author_name !== post.author ? ` (${post.display_author_name})` : ''}${post.user_title ? ` [${post.user_title}]` : ''}\n回复数(话题): ${post.posts_count - 1}\n浏览量(话题): ${post.views}\n点赞数(此贴): ${post.post_likes}\n链接: ${post.link}\n`
                ).join('\n---\n'); // Join with separator

                // Refined prompt for better structure and parsing
                const prompt = `你是一个经验丰富的论坛内容分析助手，请基于以下最新的论坛帖子列表，严格按照指定格式输出分析结果。优先分析帖子标题和元信息，如果excerpt或cooked内容简洁明确也可以参考。

帖子列表数据（每项由---分隔）：
${postSummaryText}

请根据以上数据，完成以下任务，并严格按照以下格式输出，每个部分前加上对应的中文标题和冒号，并在每个部分结束后换行，不包含额外的解释或Markdown格式（除了推荐阅读的链接格式 [标题](链接)，标题使用帖子原标题）：

总结: 简要总结这些帖子主要讨论了哪些话题，提炼核心内容。
真假分析: 基于帖子标题和你的知识，分析其中某些话题（如果涉及声明、优惠、传闻等）的可能性或真实性。请谨慎，如果无法确定，请说明这仅仅是基于有限信息进行的推测。
推荐阅读: 从列表中挑选1-2个你认为最有趣或最有价值的帖子（基于标题、回复、浏览量、点赞等信息）推荐给读者，并说明理由。请务必使用 Markdown 链接格式 [标题](链接)，标题使用帖子原标题。
辣评: 用一句话或简短的一段话，对这些帖子或论坛的整体氛围进行一个辛辣、幽默或直接的点评。
`;

                const GEMINI_API_URL = `${this.config.GEMINI_API_BASE_URL}${this.config.GEMINI_MODEL}:generateContent?key=${apiKey}`;

                // Use GM_xmlhttpRequest for cross-origin API call
                const response = await new Promise((resolve, reject) => {
                     GM_xmlhttpRequest({
                         method: 'POST',
                         url: GEMINI_API_URL,
                         headers: {
                             'Content-Type': 'application/json'
                         },
                         data: JSON.stringify({
                             contents: [{ parts: [{ text: prompt }] }],
                            generationConfig: {
                                temperature: 0.7, // Adjust creativity
                                maxOutputTokens: 1000, // Limit response length
                            }
                         }),
                         timeout: 60000, // 60 seconds timeout
                         onload: function(response) {
                             if (response.status >= 200 && response.status < 300) {
                                 try {
                                     resolve({ status: response.status, response: JSON.parse(response.responseText) });
                                 } catch (e) {
                                     reject(new Error(`Failed to parse API response JSON: ${e.message}`));
                                 }
                             } else {
                                 // Provide more details in the error message
                                 let errorDetails = `Status: ${response.status}`;
                                 if (response.statusText) errorDetails += ` ${response.statusText}`;
                                 if (response.responseText) {
                                     try {
                                         const errorJson = JSON.parse(response.responseText);
                                         if (errorJson.error && errorJson.error.message) {
                                             errorDetails += ` - ${errorJson.error.message}`;
                                             // Check for specific "suspended" message
                                             if (errorJson.error.message.includes("suspended") || errorJson.error.message.includes("denied")) {
                                                 errorDetails += "\n您的API密钥可能无效或已被暂停使用，请检查。";
                                             }
                                         } else {
                                             errorDetails += `\nBody: ${response.responseText.substring(0, 150)}...`;
                                         }
                                     } catch (_) {
                                          errorDetails += `\nBody: ${response.responseText.substring(0, 150)}...`;
                                     }
                                 }
                                 reject(new Error(`API request failed: ${errorDetails}`));
                             }
                         },
                         onerror: function(error) {
                              // The error object from GM_xmlhttpRequest might have different properties
                              const errorMsg = error.statusText || error.responseText || error.message || 'Unknown error';
                             reject(new Error(`GM_xmlhttpRequest error: ${errorMsg}`));
                         },
                         ontimeout: function() {
                             reject(new Error('API request timed out.'));
                         }
                     });
                });


                const result = response.response;
                console.log('Gemini API result:', result);

                 if (result.error) {
                     console.error('Gemini API returned error in body:', result.error);
                      let displayError = result.error.message || '未知错误';
                      if (displayError.includes("suspended") || displayError.includes("denied")) {
                         displayError += "\n您的API密钥可能无效或已被暂停使用，请检查。";
                      }
                      if (aiAnalysisDiv) this.showError(aiAnalysisDiv, `AI返回错误: ${displayError}`);
                      if (aiStatus) aiStatus.textContent = 'API错误';
                 } else {
                     const text = result.candidates?.[0]?.content?.parts?.[0]?.text;
                     if (text) {
                         console.log('Gemini Raw Output:', text);
                         this.displayAIAnalysis(text);
                         if (aiStatus) aiStatus.textContent = '已更新';
                     } else {
                         console.warn('Gemini API returned no text content or unexpected structure.', result);
                         if (aiAnalysisDiv) this.showError(aiAnalysisDiv, 'AI 生成内容为空或格式异常。');
                          if (aiStatus) aiStatus.textContent = '生成失败';
                     }
                 }

            } catch (error) {
                console.error('AI analysis error:', error);
                 const displayError = error.message.length > 200 ? error.message.substring(0, 200) + '...' : error.message;
                 if (aiAnalysisDiv) this.showError(aiAnalysisDiv, `AI分析失败: ${displayError}`);
                 if (aiStatus) aiStatus.textContent = '错误';
            } finally {
                this.state.isAnalyzing = false;
            }
        }


        displayAIAnalysis(rawText) {
             const aiAnalysisDiv = this.domElements.aiAnalysisDiv;
             if (!aiAnalysisDiv) return; // Defensive check

             // Function to robustly extract content between sections
             const getSectionContent = (text, sectionTitle) => {
                 // Escape regex special characters in the title
                 const escapedTitle = sectionTitle.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
                 // Regex: look for the title followed by :, capture content until the next known title or end of string
                 // Using lookahead for the next section title or end of string ($)
                 // Adding \s* after the colon to handle optional whitespace
                 const regex = new RegExp(`${escapedTitle}:\\s*([\\s\\S]*?)(?=\\n(?:总结|真假分析|推荐阅读|辣评):|$)$`, 'm'); // 'm' flag for multiline ^ $
                 const match = text.match(regex);
                 return match?.[1]?.trim() ?? `未能解析${sectionTitle}内容。`; // Provide fallback if not found
             };

             // Extract content for each section
             const summary = getSectionContent(rawText, '总结');
             const truthAnalysis = getSectionContent(rawText, '真假分析');
             const recommendations = getSectionContent(rawText, '推荐阅读');
             const spicyComment = getSectionContent(rawText, '辣评');

            // Clear previous content and add structure back
            aiAnalysisDiv.innerHTML = `
                <div class="linuxdo-enhancer-analysis-item">
                    <h3>总结</h3>
                    <div class="markdown-body">${this.marked.parse(summary)}</div> <!-- Use marked for all content -->
                </div>
                <div class="linuxdo-enhancer-analysis-item">
                    <h3>真假分析</h3>
                     <div class="markdown-body">${this.marked.parse(truthAnalysis)}</div> <!-- Use marked for all content -->
                </div>
                <div class="linuxdo-enhancer-analysis-item">
                    <h3>推荐阅读</h3>
                    <div class="markdown-body">${this.marked.parse(recommendations)}</div> <!-- Use marked for links and other markdown -->
                </div>
                <div class="linuxdo-enhancer-analysis-item">
                    <h3>辣评</h3>
                    <div class="markdown-body">${this.marked.parse(spicyComment)}</div> <!-- Use marked for all content -->
                </div>
            `;

             // Log if parsing failed for debugging
             if (summary.includes('未能解析') || truthAnalysis.includes('未能解析') || recommendations.includes('未能解析') || spicyComment.includes('未能解析')) {
                 console.warn("Partial or failed parsing of Gemini output. Raw text:", rawText);
             }
        }

        showLoading(element) {
             if (!element) return; // Defensive check
            element.innerHTML = `
                <div class="linuxdo-enhancer-loading">
                    <div class="linuxdo-enhancer-spinner"></div>
                    <span>加载中...</span>
                </div>
            `;
        }

        showError(element, message) {
             if (!element) return; // Defensive check
            element.innerHTML = `
                <div class="linuxdo-enhancer-error">${message}</div>
            `;
        }

        showConfigModal() {
            const apiKeyInput = this.domElements.apiKeyInput;
            const saveConfigButton = this.domElements.saveConfigButton;
            const configModal = this.domElements.configModal;
            const apiUrlSelect = this.domElements.apiUrlSelect;
            const fetchIntervalInput = this.domElements.fetchIntervalInput;
            const analyzeIntervalInput = this.domElements.analyzeIntervalInput;


             if (!apiKeyInput || !saveConfigButton || !configModal || !apiUrlSelect || !fetchIntervalInput || !analyzeIntervalInput) {
                 console.error("Config modal elements not found.");
                 return;
             }

            apiKeyInput.value = GM_getValue(this.config.API_KEY_STORAGE_KEY, '');

             // Load current interval values into inputs, convert ms to seconds
             fetchIntervalInput.value = GM_getValue(this.config.FETCH_INTERVAL_STORAGE_KEY, this.config.DEFAULT_POST_FETCH_INTERVAL) / 1000;
             analyzeIntervalInput.value = GM_getValue(this.config.ANALYZE_INTERVAL_STORAGE_KEY, this.config.DEFAULT_AI_ANALYZE_INTERVAL) / 1000;

             // Set the current API URL in the select box
             apiUrlSelect.value = this.config.GEMINI_API_BASE_URL;

            // Check initial state of save button
            const key = apiKeyInput.value.trim();
            const fetchVal = parseInt(fetchIntervalInput.value, 10);
            const analyzeVal = parseInt(analyzeIntervalInput.value, 10);
            saveConfigButton.disabled = !(key && fetchVal >= 10 && analyzeVal >= 60);


            configModal.style.display = 'flex';
        }

        hideConfigModal() {
             if (this.domElements.configModal) {
                 this.domElements.configModal.style.display = 'none';
             }
        }

        handleSaveConfig() {
            const apiKeyInput = this.domElements.apiKeyInput;
            const apiUrlSelect = this.domElements.apiUrlSelect;
            const fetchIntervalInput = this.domElements.fetchIntervalInput;
            const analyzeIntervalInput = this.domElements.analyzeIntervalInput;

            if (!apiKeyInput || !apiUrlSelect || !fetchIntervalInput || !analyzeIntervalInput) return;

            const key = apiKeyInput.value.trim();
            const apiUrl = apiUrlSelect.value;
            const fetchIntervalSec = parseInt(fetchIntervalInput.value, 10);
            const analyzeIntervalSec = parseInt(analyzeIntervalInput.value, 10);

            if (key && apiUrl && fetchIntervalSec >= 10 && analyzeIntervalSec >= 60) {
                // Save config values
                GM_setValue(this.config.API_KEY_STORAGE_KEY, key);
                GM_setValue(this.config.API_URL_STORAGE_KEY, apiUrl);
                GM_setValue(this.config.FETCH_INTERVAL_STORAGE_KEY, fetchIntervalSec * 1000); // Save in ms
                GM_setValue(this.config.ANALYZE_INTERVAL_STORAGE_KEY, analyzeIntervalSec * 1000); // Save in ms

                 // Update config in memory
                 this.config.GEMINI_API_BASE_URL = apiUrl;
                 this.config.POST_FETCH_INTERVAL = fetchIntervalSec * 1000;
                 this.config.AI_ANALYZE_INTERVAL = analyzeIntervalSec * 1000;


                this.hideConfigModal();
                this.startFetchingAndAnalyzing(); // Restart everything with the new config
                GM_notification({
                    title: 'Linux.do 增强脚本',
                    text: '设置已保存并应用！',
                    timeout: 3000
                });
            } else {
                 let errorMessage = '请填写所有必填项并确保频率值有效：\n';
                 if (!key) errorMessage += '- 请输入API密钥。\n';
                 if (!apiUrl) errorMessage += '- 请选择API地址。\n';
                 if (fetchIntervalSec < 10) errorMessage += `- 帖子刷新频率不得小于 10 秒 (当前: ${fetchIntervalSec || 0}秒)。\n`;
                 if (analyzeIntervalSec < 60) errorMessage += `- AI分析频率不得小于 60 秒 (当前: ${analyzeIntervalSec || 0}秒)。\n`;


                 GM_notification({
                     title: 'Linux.do 增强脚本',
                     text: errorMessage,
                     timeout: 5000
                 });
            }
        }
    }

    // Initialize the enhancer
    // Use a small delay to allow the main page structure to settle slightly before injecting UI
     setTimeout(() => {
         new LinuxDoEnhancer();
     }, 100); // Small delay


})();