// ==UserScript==
// @name         Ultimate Google Search Crawler Enhanced
// @namespace    http://tampermonkey.net/
// @version      6.6
// @description  고도화된 Google 검색 크롤러: 자동 페이지 네비게이션, 개선된 UI, 애니메이션 탭 추가, 강화된 중복 검사
// @author       Your Name
// @match        *://www.google.com/search*
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_xmlhttpRequest
// @grant        GM_deleteValue
// @connect      *
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    /* ===========================
       ======== Style Section =====
       =========================== */
    GM_addStyle(`
        @import url('https://fonts.googleapis.com/css2?family=Roboto&display=swap');
        @import url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css');

        body {
            font-family: 'Roboto', sans-serif;
        }

        /* 컨트롤 패널 스타일 */
        #ultimate-crawler-controls {
            position: fixed;
            top: 20px;
            right: 20px;
            background: #ffffff;
            padding: 20px;
            border: 1px solid #ddd;
            box-shadow: 0 4px 30px rgba(0,0,0,0.1);
            z-index: 10000;
            border-radius: 12px;
            width: 420px;
            max-height: 85vh;
            overflow-y: auto;
            transition: all 0.3s ease;
        }

        #ultimate-crawler-controls h2 {
            font-size: 20px;
            margin-bottom: 15px;
            text-align: center;
            color: #333;
        }

        /* 탭 메뉴 스타일 */
        #ultimate-crawler-tabs {
            display: flex;
            border-bottom: 2px solid #007BFF;
            margin-bottom: 15px;
        }

        .ultimate-crawler-tab {
            flex: 1;
            padding: 10px 0;
            text-align: center;
            cursor: pointer;
            font-size: 16px;
            color: #555;
            transition: background-color 0.3s, color 0.3s;
            user-select: none;
        }

        .ultimate-crawler-tab.active {
            background-color: #007BFF;
            color: white;
            font-weight: bold;
        }

        .ultimate-crawler-tab:hover {
            background-color: #f0f0f0;
        }

        /* 탭 컨텐츠 */
        .tab-content {
            display: none;
        }

        .tab-content.active {
            display: block;
        }

        /* 버튼 스타일 */
        .tab-content button {
            width: 100%;
            padding: 10px;
            margin: 8px 0;
            border: none;
            border-radius: 8px;
            background-color: #007BFF;
            color: white;
            font-size: 14px;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: background-color 0.3s;
        }

        .tab-content button:hover {
            background-color: #0056b3;
        }

        .tab-content button:disabled {
            background-color: #cccccc;
            cursor: not-allowed;
        }

        .tab-content button i {
            margin-right: 6px;
        }

        /* 진행률 바 스타일 */
        #ultimate-crawler-progress-container {
            margin: 15px 0;
            display: flex;
            align-items: center;
        }

        #ultimate-crawler-progress {
            width: 75%;
            background-color: #e0e0e0;
            border-radius: 10px;
            height: 18px;
            overflow: hidden;
            position: relative;
        }

        #ultimate-crawler-progress-bar {
            height: 100%;
            width: 0%;
            background-color: #4caf50;
            transition: width 0.5s;
        }

        #ultimate-crawler-percentage {
            margin-left: 10px;
            font-size: 12px;
            color: #333;
            width: 20%;
            text-align: left;
        }

        /* 상세 진행 상황 표시 */
        #ultimate-crawler-details {
            font-size: 13px;
            color: #333;
            margin-bottom: 15px;
        }

        /* 검색 필드 스타일 */
        .search-field {
            width: 100%;
            padding: 8px;
            margin-bottom: 15px;
            border: 1px solid #ccc;
            border-radius: 8px;
            font-size: 14px;
        }

        /* 설정 섹션 스타일 */
        #ultimate-crawler-settings div {
            margin-top: 15px;
        }

        #ultimate-crawler-settings label {
            display: block;
            margin: 10px 0 5px;
            font-size: 13px;
            color: #333;
        }

        #ultimate-crawler-settings input[type="text"] {
            width: 70%;
            padding: 5px;
            margin-right: 5px;
            border: 1px solid #ccc;
            border-radius: 4px;
            font-size: 13px;
        }

        #ultimate-crawler-settings button.add-btn {
            width: 25px;
            padding: 5px;
            border: none;
            background-color: #28a745;
            color: white;
            border-radius: 4px;
            cursor: pointer;
            font-size: 14px;
        }

        #ultimate-crawler-settings button.add-btn:hover {
            background-color: #218838;
        }

        #ultimate-crawler-settings ul {
            list-style: none;
            padding: 0;
            margin: 5px 0;
            max-height: 100px;
            overflow-y: auto;
            border: 1px solid #ccc;
            border-radius: 4px;
        }

        #ultimate-crawler-settings ul li {
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: 4px 8px;
            border-bottom: 1px solid #eee;
            font-size: 13px;
        }

        #ultimate-crawler-settings ul li:last-child {
            border-bottom: none;
        }

        #ultimate-crawler-settings ul li button.remove-btn {
            background: none;
            border: none;
            color: red;
            cursor: pointer;
            font-size: 14px;
        }

        /* 로그 섹션 스타일 */
        #ultimate-crawler-log {
            max-height: 300px;
            overflow-y: auto;
            padding: 10px;
            background-color: #f9f9f9;
            border: 1px solid #ddd;
            border-radius: 8px;
            font-size: 12px;
            color: #555;
        }

        /* HTML 뷰어 스타일 */
        #ultimate-crawler-html-viewer {
            display: none;
            position: fixed;
            top: 60px;
            left: 50%;
            transform: translateX(-50%);
            width: 90%;
            height: 80%;
            background: #fff;
            border: 3px solid #333;
            box-shadow: 0 4px 30px rgba(0,0,0,0.3);
            z-index: 10001;
            border-radius: 12px;
            overflow: hidden;
            flex-direction: column;
        }

        #ultimate-crawler-html-viewer-header {
            padding: 12px;
            background-color: #333;
            color: #fff;
            display: flex;
            justify-content: space-between;
            align-items: center;
            border-top-left-radius: 12px;
            border-top-right-radius: 12px;
        }

        #ultimate-crawler-html-viewer-header h4 {
            margin: 0;
            font-size: 18px;
        }

        #ultimate-crawler-html-viewer-header button {
            background: none;
            border: none;
            font-size: 22px;
            cursor: pointer;
            color: #fff;
            transition: color 0.3s;
        }

        #ultimate-crawler-html-viewer-header button:hover {
            color: #ddd;
        }

        #ultimate-crawler-html-content {
            padding: 20px;
            white-space: pre-wrap;
            word-wrap: break-word;
            overflow-y: auto;
            height: calc(100% - 60px);
            background-color: #f5f5f5;
            font-size: 14px;
            line-height: 1.5;
        }

        /* 모달 스타일 */
        #ultimate-crawler-modal {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0,0,0,0.5);
            z-index: 10002;
            justify-content: center;
            align-items: center;
        }

        #ultimate-crawler-modal-content {
            background: #fff;
            padding: 20px;
            width: 80%;
            max-width: 800px;
            border-radius: 10px;
            position: relative;
            max-height: 90vh;
            overflow-y: auto;
        }

        #ultimate-crawler-modal-content pre {
            white-space: pre-wrap;
            word-wrap: break-word;
            font-size: 14px;
            line-height: 1.5;
        }

        #ultimate-crawler-modal-close {
            position: absolute;
            top: 10px;
            right: 15px;
            background: none;
            border: none;
            font-size: 20px;
            cursor: pointer;
            color: #333;
        }

        #ultimate-crawler-modal-close:hover {
            color: #000;
        }

        /* 애니메이션 탭 스타일 */
        #ultimate-crawler-animation-tab-content {
            display: none;
        }

        #ultimate-crawler-animation-tab-content.active {
            display: block;
        }

        #ultimate-crawler-animation {
            width: 100%;
            height: 600px; /* 기존보다 크게 조정 */
            border: none;
            margin-top: 20px;
            border-radius: 8px;
        }

        /* 반응형 디자인 */
        @media (max-width: 768px) {
            #ultimate-crawler-controls {
                width: 95%;
                left: 2.5%;
                right: 2.5%;
            }

            #ultimate-crawler-animation {
                height: 500px;
            }
        }
    `);

    /* ===========================
       ======== UI Creation ========
       =========================== */
    // 컨트롤 패널 생성
    const controlPanel = document.createElement('div');
    controlPanel.id = 'ultimate-crawler-controls';
    controlPanel.innerHTML = `
        <h2 title="웹 크롤러 도구"><i class="fas fa-search"></i> 웹 크롤러</h2>
        <div id="ultimate-crawler-tabs">
            <div class="ultimate-crawler-tab active" data-tab="log" title="로그 탭"><i class="fas fa-file-alt"></i> 로그</div>
            <div class="ultimate-crawler-tab" data-tab="settings" title="설정 탭"><i class="fas fa-cog"></i> 설정</div>
            <div class="ultimate-crawler-tab" data-tab="animation" title="애니메이션 탭"><i class="fas fa-film"></i> 애니메이션</div>
        </div>
        <div id="ultimate-crawler-tab-content">
            <!-- 로그 탭 -->
            <div id="log" class="tab-content active">
                <input type="text" id="ultimate-crawler-search" class="search-field" placeholder="로그 검색..." title="로그 검색 필드">
                <div id="ultimate-crawler-log" title="크롤링 로그">
                    <!-- 크롤링 결과 표 -->
                    <table id="crawler-results" style="width: 100%; border-collapse: collapse;">
                        <thead>
                            <tr>
                                <th style="border: 1px solid #ddd; padding: 8px;">제목</th>
                                <th style="border: 1px solid #ddd; padding: 8px;">링크</th>
                                <th style="border: 1px solid #ddd; padding: 8px;">설명</th>
                                <th style="border: 1px solid #ddd; padding: 8px;">점수</th>
                                <th style="border: 1px solid #ddd; padding: 8px;">분류</th>
                                <th style="border: 1px solid #ddd; padding: 8px;">HTML 보기</th>
                            </tr>
                        </thead>
                        <tbody id="crawler-results-body">
                            <!-- 동적으로 추가될 로우 -->
                        </tbody>
                    </table>
                </div>
            </div>
            <!-- 설정 탭 -->
            <div id="settings" class="tab-content" style="display:none;">
                <button id="crawler-start" title="크롤링 시작"><i class="fas fa-play"></i> 시작</button>
                <button id="crawler-stop" disabled title="크롤링 중지"><i class="fas fa-pause"></i> 중지</button>
                <button id="crawler-reset" title="크롤링 및 설정 초기화"><i class="fas fa-sync-alt"></i> 초기화</button>
                <div id="ultimate-crawler-progress-container">
                    <div id="ultimate-crawler-progress" title="진행률 바">
                        <div id="ultimate-crawler-progress-bar"></div>
                    </div>
                    <div id="ultimate-crawler-percentage">0%</div>
                </div>
                <div id="ultimate-crawler-details">
                    <p>현재 페이지: <span id="current-page">0</span></p>
                    <p>처리된 사이트 수: <span id="processed-sites">0</span></p>
                    <p>남은 예상 시간: <span id="remaining-time">계산 불가</span> 초</p>
                </div>
                <div id="ultimate-crawler-settings">
                    <label>
                        <input type="checkbox" id="toggle-html-fetch" checked title="사이트 HTML 수집">
                        <i class="fas fa-file-code"></i> 사이트 HTML 수집
                    </label>
                    <label>
                        <input type="checkbox" id="toggle-ads-filter" checked title="광고 제외">
                        <i class="fas fa-ban"></i> 광고 제외
                    </label>
                    <label>
                        <input type="checkbox" id="toggle-logging" checked title="로그 활성화">
                        <i class="fas fa-stream"></i> 로그 활성화
                    </label>
                    <div>
                        <label>
                            🚫 블랙리스트 도메인 추가:
                            <input type="text" id="add-blacklist-domain" placeholder="example.com" title="블랙리스트 도메인 입력">
                            <button class="add-btn" id="add-blacklist-domain-btn" title="도메인 추가"><i class="fas fa-plus"></i></button>
                        </label>
                        <ul id="blacklist-domains-list"></ul>
                    </div>
                    <div>
                        <label>
                            ✅ 화이트리스트 도메인 추가:
                            <input type="text" id="add-whitelist-domain" placeholder="example.com" title="화이트리스트 도메인 입력">
                            <button class="add-btn" id="add-whitelist-domain-btn" title="도메인 추가"><i class="fas fa-plus"></i></button>
                        </label>
                        <ul id="whitelist-domains-list"></ul>
                    </div>
                    <div>
                        <label>
                            광고 도메인 추가:
                            <input type="text" id="add-ad-domain" placeholder="ads.example.com" title="광고 도메인 입력">
                            <button class="add-btn" id="add-ad-domain-btn" title="광고 도메인 추가"><i class="fas fa-plus"></i></button>
                        </label>
                        <ul id="ad-domains-list"></ul>
                    </div>
                    <div>
                        <label>
                            광고 키워드 추가:
                            <input type="text" id="add-ad-keyword" placeholder="ad" title="광고 키워드 입력">
                            <button class="add-btn" id="add-ad-keyword-btn" title="키워드 추가"><i class="fas fa-plus"></i></button>
                        </label>
                        <ul id="ad-keywords-list"></ul>
                    </div>
                    <div>
                        <label>
                            키워드 기반 제외 추가:
                            <input type="text" id="add-exclude-keyword" placeholder="keyword" title="제외 키워드 입력">
                            <button class="add-btn" id="add-exclude-keyword-btn" title="키워드 추가"><i class="fas fa-plus"></i></button>
                        </label>
                        <ul id="exclude-keywords-list"></ul>
                    </div>
                </div>
            </div>
            <!-- 애니메이션 탭 -->
            <div id="animation" class="tab-content" style="display:none;">
                <iframe id="ultimate-crawler-animation" src="https://linkkf.net/" title="애니메이션 사이트"></iframe>
            </div>
        </div>
    `;
    document.body.appendChild(controlPanel);

    // HTML 뷰어 생성
    const htmlViewer = document.createElement('div');
    htmlViewer.id = 'ultimate-crawler-html-viewer';
    htmlViewer.innerHTML = `
        <div id="ultimate-crawler-html-viewer-header">
            <h4>사이트 HTML 보기</h4>
            <button id="close-html-viewer" title="닫기"><i class="fas fa-times"></i></button>
        </div>
        <pre id="ultimate-crawler-html-content"></pre>
    `;
    document.body.appendChild(htmlViewer);

    // 모달 생성 (추가적인 HTML 표시를 위한 용도)
    const modal = document.createElement('div');
    modal.id = 'ultimate-crawler-modal';
    modal.innerHTML = `
        <div id="ultimate-crawler-modal-content">
            <button id="ultimate-crawler-modal-close" title="닫기"><i class="fas fa-times"></i></button>
            <pre id="ultimate-crawler-modal-body"></pre>
        </div>
    `;
    document.body.appendChild(modal);

    /* =============================
       ======== Variable Section ========
       ============================= */
    // 탭 요소
    const tabs = document.querySelectorAll('.ultimate-crawler-tab');
    const tabContents = document.querySelectorAll('.tab-content');

    // 버튼 요소
    const startButton = document.getElementById('crawler-start');
    const stopButton = document.getElementById('crawler-stop');
    const resetButton = document.getElementById('crawler-reset');
    const closeHtmlViewerButton = document.getElementById('close-html-viewer');
    const closeModalButton = document.getElementById('ultimate-crawler-modal-close');

    // 진행률 바 요소
    const progressBar = document.getElementById('ultimate-crawler-progress-bar');
    const percentageText = document.getElementById('ultimate-crawler-percentage');

    // 상세 진행 상황 요소
    const currentPageSpan = document.getElementById('current-page');
    const processedSitesSpan = document.getElementById('processed-sites');
    const remainingTimeSpan = document.getElementById('remaining-time');

    // 검색 필드 요소 (로그 검색)
    const searchInput = document.getElementById('ultimate-crawler-search');

    // 설정 체크박스 요소
    const htmlFetchToggle = document.getElementById('toggle-html-fetch');
    const adsFilterToggle = document.getElementById('toggle-ads-filter');
    const loggingToggle = document.getElementById('toggle-logging');

    // 블랙리스트 도메인 추가 요소
    const addBlacklistDomainInput = document.getElementById('add-blacklist-domain');
    const addBlacklistDomainBtn = document.getElementById('add-blacklist-domain-btn');
    const blacklistDomainsList = document.getElementById('blacklist-domains-list');

    // 화이트리스트 도메인 추가 요소
    const addWhitelistDomainInput = document.getElementById('add-whitelist-domain');
    const addWhitelistDomainBtn = document.getElementById('add-whitelist-domain-btn');
    const whitelistDomainsList = document.getElementById('whitelist-domains-list');

    // 광고 도메인 추가 요소
    const addAdDomainInput = document.getElementById('add-ad-domain');
    const addAdDomainBtn = document.getElementById('add-ad-domain-btn');
    const adDomainsList = document.getElementById('ad-domains-list');

    // 광고 키워드 추가 요소
    const addAdKeywordInput = document.getElementById('add-ad-keyword');
    const addAdKeywordBtn = document.getElementById('add-ad-keyword-btn');
    const adKeywordsList = document.getElementById('ad-keywords-list');

    // 키워드 기반 제외 추가 요소
    const addExcludeKeywordInput = document.getElementById('add-exclude-keyword');
    const addExcludeKeywordBtn = document.getElementById('add-exclude-keyword-btn');
    const excludeKeywordsList = document.getElementById('exclude-keywords-list');

    // 로그 창 요소
    const logWindow = document.getElementById('ultimate-crawler-log');
    const crawlerResultsBody = document.getElementById('crawler-results-body');

    // HTML 뷰어 요소
    const htmlViewerWindow = document.getElementById('ultimate-crawler-html-viewer');
    const htmlContent = document.getElementById('ultimate-crawler-html-content');

    // 모달 요소
    const modalCloseButton = document.getElementById('ultimate-crawler-modal-close');
    const modalBody = document.getElementById('ultimate-crawler-modal-body');

    // 크롤링 상태 변수
    let crawling = false;
    let currentPage = 1;
    let processedUrls = new Set();
    let crawlInterval = 1000; // 상태 업데이트 간격 (밀리초)

    // 광고 도메인, 키워드, 블랙리스트, 화이트리스트 초기 설정
    let adDomains = GM_getValue('adDomains', [
        'doubleclick.net',
        'googlesyndication.com',
        'adservice.google.com',
        'ads.youtube.com',
        'googleadservices.com',
        'ads.twitter.com',
        'facebook.com',
        'amazon-adsystem.com'
    ]);

    let adKeywords = GM_getValue('adKeywords', [
        'ad',
        'ads',
        'advertisement',
        'promotion',
        'sponsored'
    ]);

    let blacklistDomains = GM_getValue('blacklistDomains', []);
    let whitelistDomains = GM_getValue('whitelistDomains', []);
    let excludeKeywords = GM_getValue('excludeKeywords', []);

    // 저장된 크롤링 결과 (사이트 정보)
    let crawledSites = GM_getValue('crawledSites', []);

    /* =============================
       ======== Helper Functions =====
       ============================= */
    /**
     * 로그 메시지를 로그 창에 추가하는 함수
     * @param {string} message - 로그 메시지
     * @param {string} type - 메시지 타입 ('info', 'error', etc.)
     */
    function logMessage(message, type = 'info') {
        if (!loggingToggle.checked && type !== 'error') return;
        const timestamp = new Date().toLocaleTimeString();
        const formattedMessage = `[${timestamp}] [${type.toUpperCase()}] ${message}`;
        // 로그 창에 추가
        const logEntry = document.createElement('div');
        logEntry.textContent = formattedMessage;
        logEntry.style.color = (type === 'error') ? 'red' : '#555';
        logWindow.appendChild(logEntry);
        logWindow.scrollTop = logWindow.scrollHeight;
    }

    /**
     * 진행률 바를 업데이트하는 함수
     */
    function updateProgressBar() {
        // Google 검색 결과의 총 페이지 수를 동적으로 계산할 수 없으므로, 단순히 현재 페이지에 비례하여 업데이트
        const progress = (currentPage % 100) / 100 * 100; // 예시: 100페이지 주기로 진행률 리셋
        progressBar.style.width = `${progress}%`;
        percentageText.textContent = `${progress}%`;
    }

    /**
     * 상세 진행 상황을 업데이트하는 함수
     */
    function updateDetails() {
        currentPageSpan.textContent = currentPage;
        processedSitesSpan.textContent = processedUrls.size;
        // 남은 예상 시간은 페이지 수를 알 수 없으므로 '계산 불가'로 표시
        remainingTimeSpan.textContent = '계산 불가';
    }

    /**
     * 크롤링 상태를 저장하는 함수
     */
    function saveState() {
        GM_setValue('crawling', crawling);
        GM_setValue('currentPage', currentPage);
        GM_setValue('blacklistDomains', blacklistDomains);
        GM_setValue('whitelistDomains', whitelistDomains);
        GM_setValue('adDomains', adDomains);
        GM_setValue('adKeywords', adKeywords);
        GM_setValue('excludeKeywords', excludeKeywords);
        GM_setValue('processedUrls', Array.from(processedUrls));
        GM_setValue('crawledSites', crawledSites);
    }

    /**
     * 저장된 크롤링 상태를 불러오는 함수
     */
    function loadState() {
        crawling = GM_getValue('crawling', false);
        currentPage = GM_getValue('currentPage', 1);
        blacklistDomains = GM_getValue('blacklistDomains', []);
        whitelistDomains = GM_getValue('whitelistDomains', []);
        adDomains = GM_getValue('adDomains', adDomains);
        adKeywords = GM_getValue('adKeywords', adKeywords);
        excludeKeywords = GM_getValue('excludeKeywords', []);
        const processed = GM_getValue('processedUrls', []);
        processedUrls = new Set(processed);
        crawledSites = GM_getValue('crawledSites', []);
    }

    /**
     * 크롤링 상태를 초기화하는 함수
     */
    function resetState() {
        if (!confirm('모든 크롤링 데이터와 설정을 초기화하시겠습니까? 이 작업은 되돌릴 수 없습니다.')) return;

        // 모든 설정과 저장된 값 초기화
        crawling = false;
        currentPage = 1;
        processedUrls = new Set();
        crawledSites = [];
        blacklistDomains = [];
        whitelistDomains = [];
        adDomains = [
            'doubleclick.net',
            'googlesyndication.com',
            'adservice.google.com',
            'ads.youtube.com',
            'googleadservices.com',
            'ads.twitter.com',
            'facebook.com',
            'amazon-adsystem.com'
        ];
        adKeywords = [
            'ad',
            'ads',
            'advertisement',
            'promotion',
            'sponsored'
        ];
        excludeKeywords = [];

        // 값 저장 및 초기화
        saveState();

        // UI 업데이트
        blacklistDomainsList.innerHTML = '';
        whitelistDomainsList.innerHTML = '';
        adDomainsList.innerHTML = '';
        adKeywordsList.innerHTML = '';
        excludeKeywordsList.innerHTML = '';
        displayLists();

        // 로그 창 초기화
        logWindow.innerHTML = '';
        logMessage('크롤링 상태 및 설정이 초기화되었습니다.', 'info');

        // 결과 표 초기화
        crawlerResultsBody.innerHTML = '';

        // 진행률 바 및 상세 정보 초기화
        progressBar.style.width = '0%';
        percentageText.textContent = '0%';
        updateDetails();
    }

    /**
     * 저장된 목록을 UI에 표시하는 함수
     */
    function displayLists() {
        // 일반적으로 목록을 새로 고침 전에 비워줍니다.
        blacklistDomainsList.innerHTML = '';
        whitelistDomainsList.innerHTML = '';
        adDomainsList.innerHTML = '';
        adKeywordsList.innerHTML = '';
        excludeKeywordsList.innerHTML = '';

        // 블랙리스트 도메인
        blacklistDomains.forEach(domain => {
            const listItem = createListItem(domain, '블랙리스트 도메인 제거');
            blacklistDomainsList.appendChild(listItem);
        });

        // 화이트리스트 도메인
        whitelistDomains.forEach(domain => {
            const listItem = createListItem(domain, '화이트리스트 도메인 제거');
            whitelistDomainsList.appendChild(listItem);
        });

        // 광고 도메인
        adDomains.forEach(domain => {
            const listItem = createListItem(domain, '광고 도메인 제거');
            adDomainsList.appendChild(listItem);
        });

        // 광고 키워드
        adKeywords.forEach(keyword => {
            const listItem = createListItem(keyword, '광고 키워드 제거');
            adKeywordsList.appendChild(listItem);
        });

        // 제외 키워드
        excludeKeywords.forEach(keyword => {
            const listItem = createListItem(keyword, '제외 키워드 제거');
            excludeKeywordsList.appendChild(listItem);
        });
    }

    /**
     * 목록 항목을 생성하는 함수
     * @param {string} text - 목록 항목 텍스트
     * @param {string} title - 제거 버튼 툴팁
     * @returns {HTMLElement} - 생성된 리스트 아이템 요소
     */
    function createListItem(text, title) {
        const listItem = document.createElement('li');
        listItem.textContent = text;
        const removeBtn = document.createElement('button');
        removeBtn.textContent = '✖';
        removeBtn.classList.add('remove-btn');
        removeBtn.title = title;
        removeBtn.addEventListener('click', () => {
            removeListItem(text);
        });
        listItem.appendChild(removeBtn);
        return listItem;
    }

    /**
     * 목록 항목을 제거하는 함수
     * @param {string} text - 제거할 목록 항목 텍스트
     */
    function removeListItem(text) {
        // Determine which list the item belongs to
        if (blacklistDomains.includes(text)) {
            blacklistDomains = blacklistDomains.filter(d => d !== text);
            GM_setValue('blacklistDomains', blacklistDomains);
            logMessage(`블랙리스트 도메인 제거: ${text}`, 'info');
        } else if (whitelistDomains.includes(text)) {
            whitelistDomains = whitelistDomains.filter(d => d !== text);
            GM_setValue('whitelistDomains', whitelistDomains);
            logMessage(`화이트리스트 도메인 제거: ${text}`, 'info');
        } else if (adDomains.includes(text)) {
            adDomains = adDomains.filter(d => d !== text);
            GM_setValue('adDomains', adDomains);
            logMessage(`광고 도메인 제거: ${text}`, 'info');
        } else if (adKeywords.includes(text)) {
            adKeywords = adKeywords.filter(k => k !== text);
            GM_setValue('adKeywords', adKeywords);
            logMessage(`광고 키워드 제거: ${text}`, 'info');
        } else if (excludeKeywords.includes(text)) {
            excludeKeywords = excludeKeywords.filter(k => k !== text);
            GM_setValue('excludeKeywords', excludeKeywords);
            logMessage(`제외 키워드 제거: ${text}`, 'info');
        }
        // Refresh the lists in UI
        displayLists();
        // Save state
        saveState();
    }

    /**
     * 크롤링 상태를 저장하는 함수
     */
    function saveCrawledSites() {
        GM_setValue('crawledSites', crawledSites);
    }

    /**
     * 크롤링 상태를 불러오는 함수
     */
    function loadCrawledSites() {
        crawledSites.forEach(site => {
            addSiteToTable(site);
        });
    }

    /**
     * 사이트 정보를 표에 추가하는 함수
     * @param {Object} site - 사이트 정보 객체
     */
    function addSiteToTable(site) {
        const row = document.createElement('tr');

        // 제목
        const titleCell = document.createElement('td');
        titleCell.style.border = '1px solid #ddd';
        titleCell.style.padding = '8px';
        titleCell.textContent = site.title || '제목 없음';
        row.appendChild(titleCell);

        // 링크
        const linkCell = document.createElement('td');
        linkCell.style.border = '1px solid #ddd';
        linkCell.style.padding = '8px';
        const linkAnchor = document.createElement('a');
        linkAnchor.href = site.link;
        linkAnchor.textContent = site.link;
        linkAnchor.target = '_blank';
        linkAnchor.rel = 'noopener noreferrer';
        linkCell.appendChild(linkAnchor);
        row.appendChild(linkCell);

        // 설명
        const descCell = document.createElement('td');
        descCell.style.border = '1px solid #ddd';
        descCell.style.padding = '8px';
        descCell.textContent = site.description || '설명 없음';
        row.appendChild(descCell);

        // 점수
        const scoreCell = document.createElement('td');
        scoreCell.style.border = '1px solid #ddd';
        scoreCell.style.padding = '8px';
        scoreCell.textContent = site.score || 0;
        row.appendChild(scoreCell);

        // 분류
        const categoryCell = document.createElement('td');
        categoryCell.style.border = '1px solid #ddd';
        categoryCell.style.padding = '8px';
        categoryCell.textContent = site.category || 'Uncategorized';
        row.appendChild(categoryCell);

        // HTML 보기 버튼
        const htmlViewCell = document.createElement('td');
        htmlViewCell.style.border = '1px solid #ddd';
        htmlViewCell.style.padding = '8px';
        const viewButton = document.createElement('button');
        viewButton.textContent = 'HTML 보기';
        viewButton.style.padding = '4px 8px';
        viewButton.style.fontSize = '12px';
        viewButton.addEventListener('click', () => {
            if (site.html) {
                htmlContent.textContent = site.html;
                htmlViewerWindow.style.display = 'flex';
            } else {
                logMessage(`HTML 데이터가 없습니다: ${site.link}`, 'error');
            }
        });
        htmlViewCell.appendChild(viewButton);
        row.appendChild(htmlViewCell);

        crawlerResultsBody.appendChild(row);
    }

    /* =============================
       ======== Core Functions ========
       ============================= */
    /**
     * UI 상태를 업데이트하는 함수
     */
    function updateUI() {
        // 버튼 상태 업데이트
        if (crawling) {
            startButton.disabled = true;
            stopButton.disabled = false;
            resetButton.disabled = true;
        } else {
            startButton.disabled = false;
            stopButton.disabled = true;
            resetButton.disabled = false;
        }

        // 진행률 바 업데이트
        updateProgressBar();

        // 상세 진행 상황 업데이트
        updateDetails();

        // 상태 저장
        saveState();
    }

    /**
     * 크롤링을 시작하는 함수
     */
    function crawlPage() {
        if (!crawling) {
            logMessage('크롤링이 중지되었습니다.', 'info');
            updateUI();
            return;
        }

        logMessage(`페이지 ${currentPage} 크롤링 시작`, 'info');
        updateProgressBar();
        updateDetails();

        // 사이트 정보 추출
        extractSites();

        // 다음 페이지로 이동
        currentPage++;
        saveState();
        navigateToNextPage();
    }

    /**
     * 현재 페이지의 검색 결과를 추출하는 함수
     */
    function extractSites() {
        // 구글 검색 결과 선택자 (구글의 구조는 자주 변경되므로 필요 시 업데이트)
        const siteElements = document.querySelectorAll('div.g'); // 기본적인 검색 결과

        siteElements.forEach(el => {
            const titleElement = el.querySelector('h3');
            const linkElement = el.querySelector('a');
            const descriptionElement = el.querySelector('.IsZvec'); // 구글의 설명 클래스 이름 변경 가능

            const title = titleElement ? titleElement.innerText : '제목 없음';
            const link = linkElement ? linkElement.href : '주소 없음';
            const description = descriptionElement ? descriptionElement.innerText : '설명 없음';

            const site = { title, link, description };

            // 키워드를 기반으로 한 제외 검사
            if (shouldExclude(site)) {
                return;
            }

            // 광고 제외 필터
            if (adsFilterToggle.checked && !filterAds(site)) {
                logMessage(`광고 사이트 제외: ${link}`, 'info');
                return;
            }

            // 콘텐츠 품질 평가
            evaluateContentQuality(site);

            // 키워드 분류
            classifyKeywords(site);

            // 중복 검사
            if (isDuplicate(site.link)) {
                logMessage(`중복된 사이트 발견: ${link} (무시됨)`, 'info');
                return;
            }

            // HTML 수집 옵션
            if (htmlFetchToggle.checked) {
                fetchSiteHTML(site);
            }

            // 중복 URL 해싱 저장
            processedUrls.add(normalizeURL(site.link));

            // 크롤링 결과에 추가
            crawledSites.push(site);
            addSiteToTable(site);

            // 로그에 메시지 추가
            logMessage(`사이트 발견: ${site.title} | ${site.link} | 점수: ${site.score} | 분류: ${site.category || 'Uncategorized'}`, 'info');
        });

        saveState();
    }

    /**
     * 크롤링을 중지하기 전에 제외할지 판단하는 함수
     * @param {Object} site - 사이트 정보 객체
     * @returns {boolean} - 제외 여부
     */
    function shouldExclude(site) {
        if (!site.description) return false;
        const description = site.description.toLowerCase();
        for (let keyword of excludeKeywords) {
            if (description.includes(keyword.toLowerCase())) {
                logMessage(`키워드 기반 제외: ${site.link} | 키워드: ${keyword}`, 'info');
                return true;
            }
        }
        return false;
    }

    /**
     * 광고 필터링을 수행하는 함수
     * @param {Object} site - 사이트 정보 객체
     * @returns {boolean} - 광고가 아니면 true
     */
    function filterAds(site) {
        const url = site.link.toLowerCase();
        // 광고 도메인 체크
        for (let adDomain of adDomains) {
            if (url.includes(adDomain.toLowerCase())) {
                return false;
            }
        }
        return true;
    }

    /**
     * 콘텐츠 품질을 평가하는 함수
     * @param {Object} site - 사이트 정보 객체
     */
    function evaluateContentQuality(site) {
        if (!site.description) {
            site.score = 0;
            return;
        }
        const wordCount = site.description.split(/\s+/).length;
        let score = 0;
        if (wordCount > 100) score += 2;
        if (wordCount > 200) score += 3;
        site.score = score;
    }

    /**
     * 키워드 기반으로 분류하는 함수
     * @param {Object} site - 사이트 정보 객체
     */
    function classifyKeywords(site) {
        if (!site.description) {
            site.category = 'Uncategorized';
            return;
        }
        const description = site.description.toLowerCase();
        const categories = {
            'News': ['news', 'breaking', 'update'],
            'Technology': ['technology', 'tech', 'software', 'hardware'],
            'Entertainment': ['entertainment', 'movie', 'music', 'game'],
            'Health': ['health', 'medicine', 'wellness'],
            'Finance': ['finance', 'stock', 'investment']
        };
        for (let category in categories) {
            for (let keyword of categories[category]) {
                if (description.includes(keyword)) {
                    site.category = category;
                    return;
                }
            }
        }
        site.category = 'Uncategorized';
    }

    /**
     * 중복된 사이트인지 확인하는 함수
     * @param {string} url - 사이트 URL
     * @returns {boolean} - 중복 여부
     */
    function isDuplicate(url) {
        const normalized = normalizeURL(url);
        if (processedUrls.has(normalized)) {
            return true;
        }
        return false;
    }

    /**
     * URL을 정규화하여 중복 검사를 위한 함수
     * 쿼리 파라미터를 제거하여 기본 URL만 반환
     * @param {string} url - 사이트 URL
     * @returns {string} - 정규화된 URL
     */
    function normalizeURL(url) {
        try {
            const urlObj = new URL(url);
            return urlObj.origin + urlObj.pathname;
        } catch (e) {
            return url; // URL 파싱 실패 시 원본 그대로 반환
        }
    }

    /**
     * 사이트의 HTML을 수집하는 함수
     * @param {Object} site - 사이트 정보 객체
     */
    function fetchSiteHTML(site) {
        GM_xmlhttpRequest({
            method: "GET",
            url: site.link,
            onload: function(response) {
                if (response.status === 200) {
                    site.html = response.responseText;
                    logMessage(`사이트 HTML 수집 성공: ${site.link}`, 'info');
                } else {
                    site.html = null;
                    logMessage(`사이트 HTML 수집 실패 (${response.status}): ${site.link}`, 'error');
                }
                // 업데이트된 사이트 정보를 테이블에 반영
                updateSiteInTable(site);
                saveState();
            },
            onerror: function(err) {
                site.html = null;
                logMessage(`사이트 HTML 수집 오류: ${site.link}`, 'error');
                // 업데이트된 사이트 정보를 테이블에 반영
                updateSiteInTable(site);
                saveState();
            }
        });
    }

    /**
     * 테이블에서 해당 사이트의 행을 찾아 HTML 버튼을 업데이트하는 함수
     * @param {Object} site - 사이트 정보 객체
     */
    function updateSiteInTable(site) {
        const rows = crawlerResultsBody.getElementsByTagName('tr');
        for (let row of rows) {
            const linkCell = row.children[1];
            const link = linkCell.querySelector('a').href;
            if (link === site.link) {
                const htmlViewCell = row.children[5];
                // 버튼 업데이트
                if (site.html) {
                    htmlViewCell.querySelector('button').disabled = false;
                } else {
                    htmlViewCell.querySelector('button').disabled = true;
                }
                break;
            }
        }
    }

    /**
     * 다음 페이지로 이동하는 함수
     */
    function navigateToNextPage() {
        const nextButton = document.querySelector('#pnnext');
        if (nextButton) {
            nextButton.click();
            logMessage('다음 페이지로 이동 중...', 'info');
            // 페이지가 로드될 때까지 대기
            const observer = new MutationObserver(() => {
                if (document.querySelector('#search')) {
                    logMessage(`페이지 ${currentPage} 로드 완료`, 'info');
                    observer.disconnect();
                    setTimeout(() => {
                        crawlPage();
                    }, crawlInterval);
                }
            });
            observer.observe(document.body, { childList: true, subtree: true });
        } else {
            crawling = false;
            logMessage('더 이상 다음 페이지가 없어 크롤링을 종료합니다.', 'info');
            updateUI();
        }
    }

    /* =============================
       ======== Event Handlers ========
       ============================= */
    // 탭 전환 이벤트
    tabs.forEach(tab => {
        tab.addEventListener('click', () => {
            switchTab(tab.dataset.tab);
        });
    });

    /**
     * 탭을 전환하는 함수
     * @param {string} tabName - 활성화할 탭의 이름
     */
    function switchTab(tabName) {
        tabs.forEach(tab => {
            if(tab.dataset.tab === tabName){
                tab.classList.add('active');
            } else {
                tab.classList.remove('active');
            }
        });
        tabContents.forEach(content => {
            if(content.id === tabName){
                content.style.display = 'block';
                content.classList.add('active');
            } else {
                content.style.display = 'none';
                content.classList.remove('active');
            }
        });
    }

    /**
     * 크롤링 시작 버튼 클릭 이벤트
     */
    startButton.addEventListener('click', () => {
        if (crawling) return;
        crawling = true;
        logMessage('크롤링을 시작합니다.', 'info');
        updateUI();
        crawlPage();
    });

    /**
     * 크롤링 중지 버튼 클릭 이벤트
     */
    stopButton.addEventListener('click', () => {
        if (!crawling) return;
        crawling = false;
        logMessage('크롤링을 중지합니다.', 'info');
        updateUI();
    });

    /**
     * 초기화 버튼 클릭 이벤트
     */
    resetButton.addEventListener('click', () => {
        resetState();
    });

    /**
     * HTML 뷰어 닫기 버튼 클릭 이벤트
     */
    closeHtmlViewerButton.addEventListener('click', () => {
        htmlViewerWindow.style.display = 'none';
        htmlContent.textContent = '';
    });

    /**
     * 모달 닫기 버튼 클릭 이벤트
     */
    modalCloseButton.addEventListener('click', () => {
        modal.style.display = 'none';
        modalBody.textContent = '';
    });

    /**
     * 블랙리스트 도메인 추가 버튼 클릭 이벤트
     */
    addBlacklistDomainBtn.addEventListener('click', () => {
        const domain = addBlacklistDomainInput.value.trim();
        if (domain && !blacklistDomains.includes(domain)) {
            blacklistDomains.push(domain);
            GM_setValue('blacklistDomains', blacklistDomains);
            addBlacklistDomainInput.value = '';
            displayLists();
            logMessage(`블랙리스트 도메인 추가: ${domain}`, 'info');
            saveState();
        }
    });

    /**
     * 화이트리스트 도메인 추가 버튼 클릭 이벤트
     */
    addWhitelistDomainBtn.addEventListener('click', () => {
        const domain = addWhitelistDomainInput.value.trim();
        if (domain && !whitelistDomains.includes(domain)) {
            whitelistDomains.push(domain);
            GM_setValue('whitelistDomains', whitelistDomains);
            addWhitelistDomainInput.value = '';
            displayLists();
            logMessage(`화이트리스트 도메인 추가: ${domain}`, 'info');
            saveState();
        }
    });

    /**
     * 광고 도메인 추가 버튼 클릭 이벤트
     */
    addAdDomainBtn.addEventListener('click', () => {
        const domain = addAdDomainInput.value.trim();
        if (domain && !adDomains.includes(domain)) {
            adDomains.push(domain);
            GM_setValue('adDomains', adDomains);
            addAdDomainInput.value = '';
            displayLists();
            logMessage(`광고 도메인 추가: ${domain}`, 'info');
            saveState();
        }
    });

    /**
     * 광고 키워드 추가 버튼 클릭 이벤트
     */
    addAdKeywordBtn.addEventListener('click', () => {
        const keyword = addAdKeywordInput.value.trim();
        if (keyword && !adKeywords.includes(keyword)) {
            adKeywords.push(keyword);
            GM_setValue('adKeywords', adKeywords);
            addAdKeywordInput.value = '';
            displayLists();
            logMessage(`광고 키워드 추가: ${keyword}`, 'info');
            saveState();
        }
    });

    /**
     * 키워드 기반 제외 추가 버튼 클릭 이벤트
     */
    addExcludeKeywordBtn.addEventListener('click', () => {
        const keyword = addExcludeKeywordInput.value.trim();
        if (keyword && !excludeKeywords.includes(keyword)) {
            excludeKeywords.push(keyword);
            GM_setValue('excludeKeywords', excludeKeywords);
            addExcludeKeywordInput.value = '';
            displayLists();
            logMessage(`제외 키워드 추가: ${keyword}`, 'info');
            saveState();
        }
    });

    /* =============================
       ======== Initialization =======
       ============================= */
    // 상태 불러오기
    loadState();

    // UI 업데이트
    updateUI();
    displayLists();
    loadCrawledSites();

    // 페이지 로드 시 이어서 크롤링
    window.addEventListener('load', () => {
        if (crawling) {
            logMessage('페이지 로드 시 크롤링을 이어갑니다.', 'info');
            crawlPage();
        }
    });

    /* =============================
       ======== Search Functionality ======
       ============================= */
    /**
     * 로그 검색 필드 입력 이벤트
     */
    searchInput.addEventListener('input', () => {
        const filter = searchInput.value.toLowerCase();
        const rows = crawlerResultsBody.getElementsByTagName('tr');
        for (let row of rows) {
            const cells = row.getElementsByTagName('td');
            let match = false;
            for (let cell of cells) {
                if (cell.textContent.toLowerCase().includes(filter)) {
                    match = true;
                    break;
                }
            }
            row.style.display = match ? '' : 'none';
        }
    });

})();                   (https://codebeautify.org/htmlviewer 이거 hlml 보여주는 사이트 거든?? 스크립트에 이 사이트 기능 넣어서 클릭하면 바로 hlml 보게 ㄱㄱ