// ==UserScript==
// @name         이걸로 내 소원 사이트 찾으셈
// @namespace    http://tampermonkey.net/
// @version      3.3
// @description  다양한 검색 엔진의 결과를 크롤링하여 데이터를 수집하고 표시합니다. 다크 모드 지원, 에러 핸들링 강화, 크롤링 대상 필터링 옵션, 다양한 검색 엔진 지원 등 고도화된 기능을 포함합니다.
// @author       YourName
// @match        *://www.google.com/*
// @match        *://www.bing.com/*
// @match        *://duckduckgo.com/*
// @match        *://search.yahoo.com/*
// @match        *://www.baidu.com/*
// @match        *://www.ecosia.org/*
// @match        *://www.yandex.com/*
// @match        *://ask.com/*
// @match        *://www.qwant.com/*
// @match        *://뉴토끼.site/*
// @match        *://lettercounter.net/*
// @match        *://tv.54.wiKi/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addValueChangeListener
// @grant        GM_xmlhttpRequest
// @connect      *
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    // 지원하는 검색 엔진 설정
    const searchEngines = {
        'www.google.com': {
            name: 'Google',
            resultSelector: 'div.g',
            titleSelector: 'h3',
            linkSelector: 'a',
            descriptionSelector: 'span.aCOpRe, .VwiC3b',
            nextButtonSelector: '#pnnext'
        },
        'www.bing.com': {
            name: 'Bing',
            resultSelector: 'li.b_algo',
            titleSelector: 'h2',
            linkSelector: 'a',
            descriptionSelector: 'p',
            nextButtonSelector: 'a.sb_pagN'
        },
        'duckduckgo.com': {
            name: 'DuckDuckGo',
            resultSelector: 'div.result',
            titleSelector: 'h2 > a.result__a',
            linkSelector: 'a.result__a',
            descriptionSelector: '.result__snippet',
            nextButtonSelector: 'a.result--more__btn'
        },
        'search.yahoo.com': {
            name: 'Yahoo',
            resultSelector: 'div.dd.algo',
            titleSelector: 'h3.title',
            linkSelector: 'a',
            descriptionSelector: 'div.compText a.abstr',
            nextButtonSelector: 'a.next'
        },
        'www.baidu.com': {
            name: 'Baidu',
            resultSelector: 'div.result',
            titleSelector: 'h3 a',
            linkSelector: 'h3 a',
            descriptionSelector: 'div.c-abstract',
            nextButtonSelector: 'a.n'
        },
        'www.ecosia.org': {
            name: 'Ecosia',
            resultSelector: 'div.result',
            titleSelector: 'h2 a',
            linkSelector: 'h2 a',
            descriptionSelector: 'div.abstract',
            nextButtonSelector: 'a.next'
        },
        'www.yandex.com': {
            name: 'Yandex',
            resultSelector: 'li.serp-item',
            titleSelector: 'h2 a',
            linkSelector: 'h2 a',
            descriptionSelector: 'div.text-container',
            nextButtonSelector: 'a.Link_theme_outer'
        },
        'ask.com': {
            name: 'Ask',
            resultSelector: 'div.js-results-list div.Result',
            titleSelector: 'h2 a',
            linkSelector: 'h2 a',
            descriptionSelector: 'p.partial',
            nextButtonSelector: 'a.Next'
        },
        'www.qwant.com': {
            name: 'Qwant',
            resultSelector: 'div.results div.result',
            titleSelector: 'h3 a',
            linkSelector: 'h3 a',
            descriptionSelector: 'div.result__desc',
            nextButtonSelector: 'a.pagination__btn.pagination__btn--next'
        },
        '뉴토끼.site': { // 추가된 검색 엔진 설정
            name: '뉴토끼',
            resultSelector: '.search-result', // 예시 선택자: 실제 사이트의 검색 결과 컨테이너 클래스
            titleSelector: '.result-title > a', // 예시 선택자: 실제 사이트의 제목 링크 클래스
            linkSelector: '.result-title > a', // 예시 선택자: 실제 사이트의 제목 링크 클래스
            descriptionSelector: '.result-description', // 예시 선택자: 실제 사이트의 설명 클래스
            nextButtonSelector: '.pagination-next' // 예시 선택자: 실제 사이트의 다음 페이지 버튼 클래스
        },
        'lettercounter.net': { // 추가된 검색 엔진 설정
            name: 'LetterCounter',
            resultSelector: '.search-results .result-item', // 예시 선택자: 실제 사이트의 검색 결과 아이템 클래스
            titleSelector: '.result-title > a', // 예시 선택자: 실제 사이트의 제목 링크 클래스
            linkSelector: '.result-title > a', // 예시 선택자: 실제 사이트의 제목 링크 클래스
            descriptionSelector: '.result-snippet', // 예시 선택자: 실제 사이트의 설명 클래스
            nextButtonSelector: '.pagination-next' // 예시 선택자: 실제 사이트의 다음 페이지 버튼 클래스
        },
        'tv.54.wiKi': { // 추가된 TV 사이트 설정
            name: 'TV54',
            resultSelector: '.tv-search-result', // 예시 선택자: 실제 TV 사이트의 검색 결과 컨테이너 클래스
            titleSelector: '.tv-result-title > a', // 예시 선택자: 실제 TV 사이트의 제목 링크 클래스
            linkSelector: '.tv-result-title > a', // 예시 선택자: 실제 TV 사이트의 제목 링크 클래스
            descriptionSelector: '.tv-result-description', // 예시 선택자: 실제 TV 사이트의 설명 클래스
            nextButtonSelector: '.tv-pagination-next' // 예시 선택자: 실제 TV 사이트의 다음 페이지 버튼 클래스
        },
        'startpage.com': {
            name: 'Startpage',
            resultSelector: 'div.search_result',
            titleSelector: 'h3 a',
            linkSelector: 'h3 a',
            descriptionSelector: 'div.caption',
            nextButtonSelector: 'a.next'
        },
        'www.wolframalpha.com': {
            name: 'WolframAlpha',
            resultSelector: 'div.search-result',
            titleSelector: 'h2 a',
            linkSelector: 'h2 a',
            descriptionSelector: 'div.snippet',
            nextButtonSelector: 'a.next'
        }
        // 필요한 경우 추가 검색 엔진 설정 가능
    };

    // 현재 검색 엔진 감지
    const currentHost = window.location.host;
    const engine = searchEngines[currentHost];
    if (!engine) {
        console.log('지원되지 않는 검색 엔진입니다.');
        return;
    }

    // 초기화 크롤링 상태 (기존 값이 없을 경우에만 설정)
    if (GM_getValue('isCrawling') === undefined) {
        GM_setValue('isCrawling', false);
    }
    if (GM_getValue('crawlData') === undefined) {
        GM_setValue('crawlData', JSON.stringify([]));
    }
    if (GM_getValue('currentPage') === undefined) {
        GM_setValue('currentPage', 1);
    }
    // 크롤링 속도 고정
    const fixedCrawlDelay = 100; // 100ms
    if (GM_getValue('logData') === undefined) {
        GM_setValue('logData', JSON.stringify([]));
    }
    if (GM_getValue('tvPage') === undefined) {
        GM_setValue('tvPage', 54); // 초기 TV 페이지 번호 설정
    }
    if (GM_getValue('darkMode') === undefined) {
        GM_setValue('darkMode', false); // 다크 모드 초기값 설정
    }
    if (GM_getValue('filterKeywords') === undefined) {
        GM_setValue('filterKeywords', JSON.stringify([])); // 필터 키워드 초기값 설정
    }
    if (GM_getValue('blacklist') === undefined) {
        GM_setValue('blacklist', JSON.stringify([])); // 블랙리스트 초기값 설정
    }
    if (GM_getValue('whitelist') === undefined) {
        GM_setValue('whitelist', JSON.stringify([])); // 화이트리스트 초기값 설정
    }

    // UI 생성 (한 번만 생성)
    if (!document.getElementById('crawlUI')) {
        createUI();
    }

    // UI 업데이트 함수
    function updateUI() {
        const isCrawling = GM_getValue('isCrawling');
        document.getElementById('startButton').disabled = isCrawling;
        document.getElementById('stopButton').disabled = !isCrawling;
        document.getElementById('progressBar').value = getProgressPercentage();
        document.getElementById('progressText').innerText = `${getProgressPercentage()}% 완료`;
        document.getElementById('currentPageIndicator').innerText = `📄 현재 페이지: ${GM_getValue('currentPage')}`;
        document.getElementById('currentTVPageIndicator').innerText = `📺 현재 TV 페이지: ${GM_getValue('tvPage')}`;
        document.getElementById('darkModeToggle').checked = GM_getValue('darkMode');
    }

    // 크롤링 상태 변경 리스너
    GM_addValueChangeListener('isCrawling', (name, oldValue, newValue, remote) => {
        updateUI();
        log(`🔄 크롤링 상태 변경: ${newValue ? '시작됨' : '중지됨'}`);
    });

    // 다크 모드 변경 리스너
    GM_addValueChangeListener('darkMode', (name, oldValue, newValue, remote) => {
        toggleDarkMode(newValue);
        log(`🌙 다크 모드 변경: ${newValue ? '활성화됨' : '비활성화됨'}`);
    });

    // 필터 키워드 변경 리스너
    GM_addValueChangeListener('filterKeywords', (name, oldValue, newValue, remote) => {
        loadFilterKeywords();
    });

    // 화이트리스트/블랙리스트 변경 리스너
    GM_addValueChangeListener('blacklist', (name, oldValue, newValue, remote) => {
        log(`🚫 블랙리스트 업데이트: ${JSON.parse(newValue).join(', ')}`);
    });

    GM_addValueChangeListener('whitelist', (name, oldValue, newValue, remote) => {
        log(`✅ 화이트리스트 업데이트: ${JSON.parse(newValue).join(', ')}`);
    });

    // UI 생성 함수
    function createUI() {
        const container = document.createElement('div');
        container.id = 'crawlUI';
        // 컨테이너 스타일링
        container.style.position = 'fixed';
        container.style.top = '20px';
        container.style.right = '20px';
        container.style.padding = '20px';
        container.style.backgroundColor = '#FFFFFF';
        container.style.border = '1px solid #DDD';
        container.style.borderRadius = '8px';
        container.style.boxShadow = '0 4px 12px rgba(0,0,0,0.1)';
        container.style.zIndex = '10000';
        container.style.width = '500px';
        container.style.fontFamily = '"Segoe UI", Tahoma, Geneva, Verdana, sans-serif';
        container.style.fontSize = '14px';
        container.style.color = '#333';
        container.style.maxHeight = '90vh';
        container.style.overflowY = 'auto';

        // 스타일 시트 추가 (다크 모드 지원)
        const style = document.createElement('style');
        style.textContent = `
            #crawlUI h2 {
                margin: 0 0 15px 0;
                font-size: 20px;
                color: #0078D7;
                display: flex;
                align-items: center;
                justify-content: center;
            }
            #crawlUI h2 svg {
                margin-right: 8px;
            }
            #crawlUI button {
                cursor: pointer;
                transition: background-color 0.3s, transform 0.2s;
            }
            #crawlUI button:disabled {
                cursor: not-allowed;
                opacity: 0.6;
            }
            #crawlUI button:hover:not(:disabled) {
                transform: scale(1.02);
            }
            #crawlUI button:active:not(:disabled) {
                transform: scale(0.98);
            }
            #crawlUI table th {
                background-color: #f3f3f3;
                position: sticky;
                top: 0;
                z-index: 2;
                font-weight: bold;
            }
            #crawlUI table th, #crawlUI table td {
                padding: 8px;
                border: 1px solid #e1e1e1;
                text-align: left;
            }
            #crawlUI textarea {
                background-color: #f9f9f9;
            }
            .iframe-container {
                position: relative;
                padding-bottom: 56.25%;
                /* 16:9 */
                padding-top: 25px;
                height: 0;
            }
            .iframe-container iframe {
                position: absolute;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
                border: 0;
            }
            /* 다크 모드 스타일 */
            .dark-mode {
                background-color: #2E2E2E !important;
                color: #FFFFFF !important;
                border: 1px solid #555 !important;
            }
            .dark-mode textarea {
                background-color: #3E3E3E !important;
                color: #FFFFFF !important;
                border: 1px solid #555 !important;
            }
            .dark-mode table th, .dark-mode table td {
                border: 1px solid #555 !important;
            }
            .dark-mode #crawlUI {
                background-color: #2E2E2E !important;
                border: 1px solid #555 !important;
            }
            /* 스크롤바 스타일 */
            #crawlUI::-webkit-scrollbar {
                width: 8px;
            }
            #crawlUI::-webkit-scrollbar-track {
                background: #f1f1f1;
            }
            #crawlUI::-webkit-scrollbar-thumb {
                background: #ccc;
                border-radius: 4px;
            }
            #crawlUI::-webkit-scrollbar-thumb:hover {
                background: #aaa;
            }
            /* 필터 input 스타일 */
            #filterInput {
                width: 100%;
                padding: 8px;
                margin-bottom: 10px;
                border: 1px solid #CCC;
                border-radius: 4px;
                box-sizing: border-box;
                font-size: 14px;
            }
            /* 설정 탭 스타일 */
            #settingsTab {
                width: 100%;
                padding: 10px;
                margin-bottom: 15px;
                background-color: #f3f3f3;
                border: 1px solid #ddd;
                border-radius: 4px;
                font-weight: bold;
                cursor: pointer;
                text-align: center;
            }
            #settingsContent {
                display: none;
                margin-bottom: 15px;
            }
            #settingsContent.visible {
                display: block;
            }
            .settings-section {
                margin-bottom: 10px;
            }
            .settings-section label {
                display: block;
                margin-bottom: 5px;
                font-weight: bold;
            }
            .settings-section input {
                width: 100%;
                padding: 6px;
                border: 1px solid #CCC;
                border-radius: 4px;
                box-sizing: border-box;
                font-size: 14px;
                margin-bottom: 5px;
            }
            /* 테이블 스크롤 */
            #crawlResultsContainer {
                max-height: 300px;
                overflow-y: auto;
                border: 1px solid #e1e1e1;
                border-radius: 4px;
                margin-bottom: 15px;
            }
        `;
        container.appendChild(style);

        // 제목
        const title = document.createElement('h2');
        title.innerHTML = `
            <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24" fill="#0078D7">
                <path d="M0 0h24v24H0V0z" fill="none"/>
                <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48
                10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.95-7-7.93
                0-3.87 3.13-7 7-7s7 3.13 7 7c0 3.98-3.05 7.44-7
                7.93v-2.93h0zm0-12c-2.76 0-5 2.24-5 5s2.24
                5 5 5 5-2.24 5-5-2.24-5-5-5z"/>
            </svg>
            🔍 검색 엔진 크롤러
        `;
        container.appendChild(title);

        // 설정 탭
        const settingsTab = document.createElement('div');
        settingsTab.id = 'settingsTab';
        settingsTab.innerText = '⚙️ 설정';
        settingsTab.onclick = () => {
            settingsContent.classList.toggle('visible');
        };
        container.appendChild(settingsTab);

        // 설정 내용
        const settingsContent = document.createElement('div');
        settingsContent.id = 'settingsContent';

        // 화이트리스트
        const whitelistSection = document.createElement('div');
        whitelistSection.classList.add('settings-section');
        const whitelistLabel = document.createElement('label');
        whitelistLabel.setAttribute('for', 'whitelistInput');
        whitelistLabel.innerText = '✅ 화이트리스트 도메인 (콤마로 구분)';
        whitelistSection.appendChild(whitelistLabel);
        const whitelistInput = document.createElement('input');
        whitelistInput.type = 'text';
        whitelistInput.id = 'whitelistInput';
        whitelistInput.value = GM_getValue('whitelist') ? JSON.parse(GM_getValue('whitelist')).join(', ') : '';
        whitelistInput.placeholder = '예: example.com, another.com';
        whitelistInput.onchange = () => {
            const domains = whitelistInput.value.split(',').map(d => d.trim()).filter(d => d.length > 0);
            GM_setValue('whitelist', JSON.stringify(domains));
            log(`✅ 화이트리스트 업데이트: ${domains.join(', ')}`);
        };
        whitelistSection.appendChild(whitelistInput);
        settingsContent.appendChild(whitelistSection);

        // 블랙리스트
        const blacklistSection = document.createElement('div');
        blacklistSection.classList.add('settings-section');
        const blacklistLabel = document.createElement('label');
        blacklistLabel.setAttribute('for', 'blacklistInput');
        blacklistLabel.innerText = '🚫 블랙리스트 도메인 (콤마로 구분)';
        blacklistSection.appendChild(blacklistLabel);
        const blacklistInput = document.createElement('input');
        blacklistInput.type = 'text';
        blacklistInput.id = 'blacklistInput';
        blacklistInput.value = GM_getValue('blacklist') ? JSON.parse(GM_getValue('blacklist')).join(', ') : '';
        blacklistInput.placeholder = '예: spam.com, ads.com';
        blacklistInput.onchange = () => {
            const domains = blacklistInput.value.split(',').map(d => d.trim()).filter(d => d.length > 0);
            GM_setValue('blacklist', JSON.stringify(domains));
            log(`🚫 블랙리스트 업데이트: ${domains.join(', ')}`);
        };
        blacklistSection.appendChild(blacklistInput);
        settingsContent.appendChild(blacklistSection);

        container.appendChild(settingsContent);

        // 다크 모드 토글
        const darkModeContainer = document.createElement('div');
        darkModeContainer.style.marginBottom = '15px';
        darkModeContainer.style.display = 'flex';
        darkModeContainer.style.alignItems = 'center';
        darkModeContainer.style.justifyContent = 'space-between';

        const darkModeLabel = document.createElement('label');
        darkModeLabel.innerText = '🌙 다크 모드';
        darkModeLabel.setAttribute('for', 'darkModeToggle');
        darkModeLabel.style.fontWeight = 'bold';
        darkModeContainer.appendChild(darkModeLabel);

        const darkModeToggle = document.createElement('input');
        darkModeToggle.type = 'checkbox';
        darkModeToggle.id = 'darkModeToggle';
        darkModeToggle.checked = GM_getValue('darkMode');
        darkModeToggle.style.transform = 'scale(1.5)';
        darkModeToggle.onchange = () => {
            GM_setValue('darkMode', darkModeToggle.checked);
        };
        darkModeContainer.appendChild(darkModeToggle);

        container.appendChild(darkModeContainer);

        // 크롤링 대상 필터링 옵션
        const filterContainer = document.createElement('div');
        filterContainer.style.marginBottom = '15px';

        const filterLabel = document.createElement('label');
        filterLabel.innerText = '🔍 테이블 검색 필터';
        filterLabel.setAttribute('for', 'filterInput');
        filterLabel.style.display = 'block';
        filterLabel.style.marginBottom = '5px';
        filterLabel.style.fontWeight = 'bold';
        filterContainer.appendChild(filterLabel);

        const filterInput = document.createElement('input');
        filterInput.type = 'text';
        filterInput.id = 'filterInput';
        filterInput.placeholder = '키워드를 입력하세요...';
        filterInput.style.width = '100%';
        filterInput.style.padding = '8px';
        filterInput.style.border = '1px solid #CCC';
        filterInput.style.borderRadius = '4px';
        filterInput.style.boxSizing = 'border-box';
        filterInput.style.fontSize = '14px';
        filterInput.oninput = () => {
            filterTable(filterInput.value);
        };
        filterContainer.appendChild(filterInput);
        container.appendChild(filterContainer);

        // 버튼 생성
        const buttonContainer = document.createElement('div');
        buttonContainer.style.display = 'flex';
        buttonContainer.style.justifyContent = 'space-between';
        buttonContainer.style.marginBottom = '15px';

        const startButton = document.createElement('button');
        startButton.id = 'startButton';
        startButton.innerHTML = '▶️ 시작';
        startButton.style.flex = '1';
        startButton.style.marginRight = '5px';
        startButton.style.padding = '10px';
        startButton.style.backgroundColor = '#0078D7';
        startButton.style.color = '#FFF';
        startButton.style.border = 'none';
        startButton.style.borderRadius = '4px';
        startButton.style.fontSize = '14px';
        startButton.style.transition = 'background-color 0.3s';
        startButton.onmouseenter = () => {
            startButton.style.backgroundColor = '#005a9e';
        };
        startButton.onmouseleave = () => {
            startButton.style.backgroundColor = '#0078D7';
        };
        startButton.onclick = startCrawling;
        buttonContainer.appendChild(startButton);

        const stopButton = document.createElement('button');
        stopButton.id = 'stopButton';
        stopButton.innerHTML = '⏹️ 중지';
        stopButton.style.flex = '1';
        stopButton.style.marginLeft = '5px';
        stopButton.style.padding = '10px';
        stopButton.style.backgroundColor = '#d9534f';
        stopButton.style.color = '#FFF';
        stopButton.style.border = 'none';
        stopButton.style.borderRadius = '4px';
        stopButton.style.fontSize = '14px';
        stopButton.style.transition = 'background-color 0.3s';
        stopButton.onmouseenter = () => {
            stopButton.style.backgroundColor = '#c9302c';
        };
        stopButton.onmouseleave = () => {
            stopButton.style.backgroundColor = '#d9534f';
        };
        stopButton.onclick = stopCrawling;
        stopButton.disabled = !GM_getValue('isCrawling');
        buttonContainer.appendChild(stopButton);

        container.appendChild(buttonContainer);

        // 초기화 버튼
        const resetButton = document.createElement('button');
        resetButton.innerHTML = '🔄 초기화';
        resetButton.style.width = '100%';
        resetButton.style.padding = '10px';
        resetButton.style.backgroundColor = '#6c757d';
        resetButton.style.color = '#FFF';
        resetButton.style.border = 'none';
        resetButton.style.borderRadius = '4px';
        resetButton.style.fontSize = '14px';
        resetButton.style.cursor = 'pointer';
        resetButton.style.transition = 'background-color 0.3s';
        resetButton.onmouseenter = () => {
            resetButton.style.backgroundColor = '#5a6268';
        };
        resetButton.onmouseleave = () => {
            resetButton.style.backgroundColor = '#6c757d';
        };
        resetButton.style.marginBottom = '15px';
        resetButton.onclick = resetCrawling;
        container.appendChild(resetButton);

        // 진행률 표시
        const progressContainer = document.createElement('div');
        progressContainer.style.marginBottom = '15px';

        const progressLabel = document.createElement('label');
        progressLabel.innerText = '📊 진행률';
        progressLabel.style.display = 'block';
        progressLabel.style.marginBottom = '5px';
        progressLabel.style.fontWeight = 'bold';
        progressContainer.appendChild(progressLabel);

        const progressBar = document.createElement('progress');
        progressBar.id = 'progressBar';
        progressBar.value = getProgressPercentage();
        progressBar.max = 100;
        progressBar.style.width = '100%';
        progressBar.style.height = '20px';
        progressBar.style.borderRadius = '4px';
        progressBar.style.marginBottom = '5px';
        progressContainer.appendChild(progressBar);

        const progressText = document.createElement('span');
        progressText.id = 'progressText';
        progressText.innerText = `${getProgressPercentage()}% 완료`;
        progressText.style.fontWeight = 'bold';
        progressContainer.appendChild(progressText);

        container.appendChild(progressContainer);

        // 현재 페이지 표시
        const pageIndicator = document.createElement('div');
        pageIndicator.id = 'currentPageIndicator';
        pageIndicator.innerText = `📄 현재 페이지: ${GM_getValue('currentPage')}`;
        pageIndicator.style.marginBottom = '15px';
        pageIndicator.style.fontWeight = 'bold';
        container.appendChild(pageIndicator);

        // 현재 TV 페이지 표시
        const tvPageIndicator = document.createElement('div');
        tvPageIndicator.id = 'currentTVPageIndicator';
        tvPageIndicator.innerText = `📺 현재 TV 페이지: ${GM_getValue('tvPage')}`;
        tvPageIndicator.style.marginBottom = '15px';
        tvPageIndicator.style.fontWeight = 'bold';
        container.appendChild(tvPageIndicator);

        // 로그 창
        const logContainer = document.createElement('div');
        logContainer.style.marginBottom = '15px';

        const logTitle = document.createElement('h3');
        logTitle.innerText = '📝 로그';
        logTitle.style.marginBottom = '10px';
        logTitle.style.fontSize = '16px';
        logTitle.style.color = '#333';
        logContainer.appendChild(logTitle);

        const logArea = document.createElement('textarea');
        logArea.id = 'crawlLog';
        logArea.style.width = '100%';
        logArea.style.height = '120px';
        logArea.style.padding = '10px';
        logArea.style.border = '1px solid #CCC';
        logArea.style.borderRadius = '4px';
        logArea.style.resize = 'none';
        logArea.style.fontFamily = '"Segoe UI", Tahoma, Geneva, Verdana, sans-serif';
        logArea.style.fontSize = '12px';
        logArea.style.backgroundColor = '#f9f9f9';
        logArea.style.color = '#333';
        logArea.disabled = true;
        logContainer.appendChild(logArea);

        container.appendChild(logContainer);

        // 결과 테이블 생성
        const tableContainer = document.createElement('div');
        tableContainer.id = 'crawlResultsContainer';
        tableContainer.style.marginBottom = '15px';
        tableContainer.style.maxHeight = '300px';
        tableContainer.style.overflowY = 'auto';
        tableContainer.style.border = '1px solid #e1e1e1';
        tableContainer.style.borderRadius = '4px';
        tableContainer.style.padding = '5px';
        tableContainer.style.backgroundColor = '#FFF';

        const table = document.createElement('table');
        table.id = 'crawlResults';
        table.style.width = '100%';
        table.style.borderCollapse = 'collapse';

        const thead = document.createElement('thead');
        const headerRow = document.createElement('tr');
        ['📰 사이트 이름', '🔗 주소', '📝 설명', '🗨️ 텍스트 내용', '🗂️ 분류', '📄 HTML 보기'].forEach(text => {
            const th = document.createElement('th');
            th.innerText = text;
            th.style.backgroundColor = '#f3f3f3';
            th.style.position = 'sticky';
            th.style.top = '0';
            th.style.zIndex = '1';
            th.style.border = '1px solid #e1e1e1';
            th.style.padding = '8px';
            thead.appendChild(th);
        });
        table.appendChild(thead);

        const tbody = document.createElement('tbody');
        tbody.id = 'crawlTableBody';
        table.appendChild(tbody);

        tableContainer.appendChild(table);
        container.appendChild(tableContainer);

        // HTML 뷰어 섹션 추가
        const htmlViewerContainer = document.createElement('div');
        htmlViewerContainer.id = 'htmlViewerContainer';
        htmlViewerContainer.style.display = 'none';
        htmlViewerContainer.style.marginBottom = '15px';
        htmlViewerContainer.style.border = '1px solid #e1e1e1';
        htmlViewerContainer.style.borderRadius = '4px';
        htmlViewerContainer.style.padding = '10px';
        htmlViewerContainer.style.backgroundColor = '#FFF';

        const htmlViewerTitle = document.createElement('h3');
        htmlViewerTitle.innerText = '📄 HTML 뷰어';
        htmlViewerTitle.style.marginBottom = '10px';
        htmlViewerTitle.style.fontSize = '16px';
        htmlViewerTitle.style.color = '#333';
        htmlViewerContainer.appendChild(htmlViewerTitle);

        const closeViewerButton = document.createElement('button');
        closeViewerButton.innerHTML = '❌ 닫기';
        closeViewerButton.style.marginBottom = '10px';
        closeViewerButton.style.padding = '6px 12px';
        closeViewerButton.style.backgroundColor = '#d9534f';
        closeViewerButton.style.color = '#FFF';
        closeViewerButton.style.border = 'none';
        closeViewerButton.style.borderRadius = '4px';
        closeViewerButton.style.cursor = 'pointer';
        closeViewerButton.style.fontSize = '14px';
        closeViewerButton.style.transition = 'background-color 0.3s';
        closeViewerButton.onmouseenter = () => {
            closeViewerButton.style.backgroundColor = '#c9302c';
        };
        closeViewerButton.onmouseleave = () => {
            closeViewerButton.style.backgroundColor = '#d9534f';
        };
        closeViewerButton.onclick = () => {
            htmlViewerContainer.style.display = 'none';
            htmlViewerContent.src = '';
        };
        htmlViewerContainer.appendChild(closeViewerButton);

        const htmlViewerContent = document.createElement('iframe');
        htmlViewerContent.id = 'htmlViewerContent';
        htmlViewerContent.style.width = '100%';
        htmlViewerContent.style.height = '300px';
        htmlViewerContent.style.border = '1px solid #CCC';
        htmlViewerContent.style.borderRadius = '4px';
        htmlViewerContent.style.marginTop = '10px';
        htmlViewerContainer.appendChild(htmlViewerContent);

        container.appendChild(htmlViewerContainer);

        // 애니메이션 사이트 뷰어 추가
        const animeViewerContainer = document.createElement('div');
        animeViewerContainer.id = 'animeViewerContainer';
        animeViewerContainer.style.marginBottom = '15px';
        animeViewerContainer.style.border = '1px solid #e1e1e1';
        animeViewerContainer.style.borderRadius = '4px';
        animeViewerContainer.style.padding = '10px';
        animeViewerContainer.style.backgroundColor = '#FFF';

        const animeViewerTitle = document.createElement('h3');
        animeViewerTitle.innerText = '🎬 애니메이션 보기';
        animeViewerTitle.style.marginBottom = '10px';
        animeViewerTitle.style.fontSize = '16px';
        animeViewerTitle.style.color = '#333';
        animeViewerContainer.appendChild(animeViewerTitle);

        const animeViewerFrame = document.createElement('iframe');
        animeViewerFrame.src = 'https://linkkf.net/';
        animeViewerFrame.style.width = '100%';
        animeViewerFrame.style.height = '300px';
        animeViewerFrame.style.border = '1px solid #CCC';
        animeViewerFrame.style.borderRadius = '4px';
        animeViewerFrame.style.marginTop = '10px';
        animeViewerContainer.appendChild(animeViewerFrame);

        container.appendChild(animeViewerContainer);

        // TV 사이트 뷰어 추가
        const tvViewerContainer = document.createElement('div');
        tvViewerContainer.id = 'tvViewerContainer';
        tvViewerContainer.style.marginBottom = '15px';
        tvViewerContainer.style.border = '1px solid #e1e1e1';
        tvViewerContainer.style.borderRadius = '4px';
        tvViewerContainer.style.padding = '10px';
        tvViewerContainer.style.backgroundColor = '#FFF';

        const tvViewerTitle = document.createElement('h3');
        tvViewerTitle.innerText = '📺 TV 사이트 보기';
        tvViewerTitle.style.marginBottom = '10px';
        tvViewerTitle.style.fontSize = '16px';
        tvViewerTitle.style.color = '#333';
        tvViewerContainer.appendChild(tvViewerTitle);

        const tvViewerFrame = document.createElement('iframe');
        tvViewerFrame.id = 'tvViewerFrame';
        tvViewerFrame.src = `https://tv.${GM_getValue('tvPage')}.wiKi/`;
        tvViewerFrame.style.width = '100%';
        tvViewerFrame.style.height = '300px';
        tvViewerFrame.style.border = '1px solid #CCC';
        tvViewerFrame.style.borderRadius = '4px';
        tvViewerFrame.style.marginTop = '10px';
        tvViewerFrame.onload = () => {
            // 페이지가 성공적으로 로드되면 로그에 기록
            log(`✅ TV 사이트 로드 완료: https://tv.${GM_getValue('tvPage')}.wiKi/`);
        };
        tvViewerFrame.onerror = () => {
            // 페이지 로드 실패 시 로그에 기록
            log(`❌ TV 사이트 로드 실패: https://tv.${GM_getValue('tvPage')}.wiKi/`);
            alert(`TV 사이트 로드 실패: https://tv.${GM_getValue('tvPage')}.wiKi/`);
        };
        tvViewerContainer.appendChild(tvViewerFrame);

        // 새로 고침 버튼 추가
        const refreshTVButton = document.createElement('button');
        refreshTVButton.innerHTML = '🔄 TV 사이트 새로 고침';
        refreshTVButton.style.marginTop = '10px';
        refreshTVButton.style.padding = '8px 12px';
        refreshTVButton.style.backgroundColor = '#28a745';
        refreshTVButton.style.color = '#FFF';
        refreshTVButton.style.border = 'none';
        refreshTVButton.style.borderRadius = '4px';
        refreshTVButton.style.cursor = 'pointer';
        refreshTVButton.style.fontSize = '14px';
        refreshTVButton.style.transition = 'background-color 0.3s';
        refreshTVButton.onmouseenter = () => {
            refreshTVButton.style.backgroundColor = '#218838';
        };
        refreshTVButton.onmouseleave = () => {
            refreshTVButton.style.backgroundColor = '#28a745';
        };
        refreshTVButton.onclick = refreshTVSite;
        tvViewerContainer.appendChild(refreshTVButton);

        container.appendChild(tvViewerContainer);

        document.body.appendChild(container);

        // 다크 모드 초기 설정
        toggleDarkMode(GM_getValue('darkMode'));

        // 초기 테이블 데이터 로드
        loadCrawlData();
        loadLogData();
        loadFilterKeywords();

        // 크롤링 계속 진행하기 위한 자동 실행
        if (GM_getValue('isCrawling')) {
            setTimeout(() => {
                crawlPage();
            }, fixedCrawlDelay);
        }
    }

    // 다크 모드 토글 함수
    function toggleDarkMode(isDark) {
        const container = document.getElementById('crawlUI');
        if (isDark) {
            container.classList.add('dark-mode');
        } else {
            container.classList.remove('dark-mode');
        }
    }

    // 크롤링 시작 함수
    function startCrawling() {
        if (GM_getValue('isCrawling')) return;
        GM_setValue('isCrawling', true);
        log('🔍 크롤링 시작');
        if (GM_getValue('currentPage') === 1) {
            GM_setValue('crawlData', JSON.stringify([]));
            clearTable();
            clearLog();
        }
        crawlPage();
    }

    // 크롤링 중지 함수
    function stopCrawling() {
        if (!GM_getValue('isCrawling')) return;
        GM_setValue('isCrawling', false);
        log('⏹️ 크롤링 중지');
    }

    // 크롤링 초기화 함수
    function resetCrawling() {
        stopCrawling();
        GM_setValue('crawlData', JSON.stringify([]));
        GM_setValue('currentPage', 1);
        GM_setValue('tvPage', 54); // TV 페이지 번호 초기화
        clearTable();
        clearLog();
        log('🔄 크롤링 초기화 완료');
    }

    // 테이블 초기화
    function clearTable() {
        const tbody = document.getElementById('crawlTableBody');
        if (tbody) {
            tbody.innerHTML = '';
        }
    }

    // 로그 초기화
    function clearLog() {
        GM_setValue('logData', JSON.stringify([]));
        loadLogData();
    }

    // 크롤링 데이터 로드 및 테이블 업데이트
    function loadCrawlData() {
        const crawlData = JSON.parse(GM_getValue('crawlData'));
        crawlData.forEach(item => {
            addRowToTable(item.title, item.url, item.description, item.quality, item.classification);
        });
    }

    // 로그 데이터 로드 및 로그 표시
    function loadLogData() {
        const logData = JSON.parse(GM_getValue('logData'));
        const logArea = document.getElementById('crawlLog');
        logArea.value = logData.join('\n');
        logArea.scrollTop = logArea.scrollHeight;
    }

    // 필터 키워드 로드
    function loadFilterKeywords() {
        // 현재 로직은 필터링이 실시간으로 적용되므로 별도의 로드가 필요 없음
    }

    // 로그 추가 함수
    function log(message) {
        const timestamp = new Date().toLocaleTimeString();
        const logEntry = `[${timestamp}] ${message}`;
        let logData = JSON.parse(GM_getValue('logData'));
        logData.push(logEntry);
        GM_setValue('logData', JSON.stringify(logData));

        const logArea = document.getElementById('crawlLog');
        logArea.value += logEntry + '\n';
        logArea.scrollTop = logArea.scrollHeight;
    }

    // 진행률 계산 함수
    function getProgressPercentage() {
        // 최대 페이지 수를 100으로 설정
        const maxPages = 100;
        const currentPage = GM_getValue('currentPage');
        return Math.min(Math.floor((currentPage / maxPages) * 100), 100);
    }

    // 크롤링 페이지 처리
    function crawlPage() {
        if (!GM_getValue('isCrawling')) return;

        log(`📄 페이지 ${GM_getValue('currentPage')} 크롤링 중...`);

        let crawlData = JSON.parse(GM_getValue('crawlData'));
        const results = document.querySelectorAll(engine.resultSelector);

        const filterKeywords = GM_getValue('filterKeywords') ? JSON.parse(GM_getValue('filterKeywords')) : [];
        const blacklist = GM_getValue('blacklist') ? JSON.parse(GM_getValue('blacklist')) : [];
        const whitelist = GM_getValue('whitelist') ? JSON.parse(GM_getValue('whitelist')) : [];

        // 화이트리스트가 설정된 경우
        if (whitelist.length > 0 && !whitelist.includes(currentHost)) {
            log('✅ 화이트리스트에 포함되지 않은 도메인, 크롤링 중지.');
            GM_setValue('isCrawling', false);
            log('🏁 크롤링 완료');
            alert('크롤링이 완료되었습니다!');
            return;
        }

        results.forEach(result => {
            // 블랙리스트 도메인 필터링
            const linkElement = result.querySelector(engine.linkSelector);
            if (linkElement) {
                const url = new URL(linkElement.href);
                const domain = url.hostname.replace('www.', '');
                if (blacklist.includes(domain)) {
                    log(`🚫 블랙리스트 도메인 필터링: ${domain}`);
                    return;
                }
            }

            // 광고 제외 로직 (예: 특정 클래스나 도메인 필터링)
            if (isAd(result)) return;

            const titleElement = result.querySelector(engine.titleSelector);
            const linkEl = result.querySelector(engine.linkSelector);
            const descriptionElement = result.querySelector(engine.descriptionSelector);

            if (titleElement && linkEl) {
                const title = titleElement.innerText.trim();
                const url = linkEl.href;
                const description = descriptionElement ? descriptionElement.innerText.trim() : '';

                // 필터 키워드 체크
                if (filterKeywords.length > 0 && filterKeywords.some(kw => title.includes(kw) || description.includes(kw))) {
                    log(`🚫 필터링됨: ${title} - ${url}`);
                    return;
                }

                // 중복 검사
                if (!crawlData.some(item => item.url === url)) {
                    const quality = evaluateContentQuality(description);
                    const classification = classifyKeywords(description);
                    crawlData.push({ title, url, description, quality, classification });
                    addRowToTable(title, url, description, quality, classification);
                    log(`✅ 데이터 수집: ${title} - ${url}`);
                }
            }
        });

        GM_setValue('crawlData', JSON.stringify(crawlData));
        updateUI();

        // 다음 페이지로 이동
        const nextButton = document.querySelector(engine.nextButtonSelector);
        if (nextButton && GM_getValue('currentPage') < 100 && GM_getValue('isCrawling')) { // 최대 페이지 수 설정
            const nextPageUrl = nextButton.href;
            if (nextPageUrl) {
                GM_setValue('currentPage', GM_getValue('currentPage') + 1);
                updateUI();
                log(`➡️ 다음 페이지로 이동: 페이지 ${GM_getValue('currentPage')}`);
                setTimeout(() => {
                    window.location.href = nextPageUrl;
                }, fixedCrawlDelay); // 고정 크롤링 속도 사용 (100ms)
            } else {
                log('❌ 다음 페이지 URL을 찾을 수 없습니다.');
                GM_setValue('isCrawling', false);
                log('🏁 크롤링 완료');
                alert('크롤링이 완료되었습니다!');
            }
        } else {
            GM_setValue('isCrawling', false);
            log('🏁 크롤링 완료');
            alert('크롤링이 완료되었습니다!');
        }
    }

    // 테이블에 데이터 추가
    function addRowToTable(title, url, description, quality, classification) {
        const tbody = document.getElementById('crawlTableBody');
        if (!tbody) return;

        const row = tbody.insertRow();

        const titleCell = row.insertCell();
        titleCell.innerText = title;
        titleCell.style.fontWeight = 'bold';

        const urlCell = row.insertCell();
        const link = document.createElement('a');
        link.href = url;
        link.innerText = url;
        link.target = '_blank';
        link.style.color = '#0078D7';
        urlCell.appendChild(link);

        const descCell = row.insertCell();
        descCell.innerText = description;
        descCell.style.color = '#555';

        const qualityCell = row.insertCell();
        qualityCell.innerText = quality;
        qualityCell.style.color = quality > 100 ? '#28a745' : '#dc3545';
        qualityCell.style.fontWeight = 'bold';

        const classificationCell = row.insertCell();
        classificationCell.innerText = classification;
        classificationCell.style.fontWeight = 'bold';

        const viewHtmlCell = row.insertCell();
        const viewHtmlButton = document.createElement('button');
        viewHtmlButton.innerHTML = '📄 HTML 보기';
        viewHtmlButton.style.padding = '6px 10px';
        viewHtmlButton.style.backgroundColor = '#28a745';
        viewHtmlButton.style.color = '#FFF';
        viewHtmlButton.style.border = 'none';
        viewHtmlButton.style.borderRadius = '4px';
        viewHtmlButton.style.cursor = 'pointer';
        viewHtmlButton.style.fontSize = '12px';
        viewHtmlButton.style.transition = 'background-color 0.3s';
        viewHtmlButton.onmouseenter = () => {
            viewHtmlButton.style.backgroundColor = '#218838';
        };
        viewHtmlButton.onmouseleave = () => {
            viewHtmlButton.style.backgroundColor = '#28a745';
        };
        viewHtmlButton.onclick = () => viewHTML(url);
        viewHtmlCell.appendChild(viewHtmlButton);
    }

    // HTML 뷰어 열기
    function viewHTML(url) {
        log(`🔗 HTML 요청: ${url}`);
        GM_xmlhttpRequest({
            method: 'GET',
            url: url,
            onload: function(response) {
                if (response.status >= 200 && response.status < 300) {
                    displayHTML(response.responseText, url);
                    log(`✅ HTML 로드 완료: ${url}`);
                } else {
                    log(`❌ HTML 로드 실패 (${response.status}): ${url}`);
                    alert(`HTML 로드 실패: ${response.status} ${response.statusText}`);
                }
            },
            onerror: function(err) {
                log(`⚠️ HTML 요청 에러: ${url}`);
                alert(`HTML 요청 에러: ${err}`);
            }
        });
    }

    // HTML을 뷰어에 표시
    function displayHTML(htmlContent, url) {
        const htmlViewerContainer = document.getElementById('htmlViewerContainer');
        const htmlViewerContent = document.getElementById('htmlViewerContent');

        // Blob을 사용하여 안전하게 iframe에 로드
        const blob = new Blob([htmlContent], { type: 'text/html' });
        const blobURL = URL.createObjectURL(blob);
        htmlViewerContent.src = blobURL;

        htmlViewerContainer.style.display = 'block';
    }

    // TV 사이트 새로 고침 함수
    function refreshTVSite() {
        let currentTVPage = GM_getValue('tvPage');
        currentTVPage += 1;
        GM_setValue('tvPage', currentTVPage);
        const tvFrame = document.getElementById('tvViewerFrame');
        tvFrame.src = `https://tv.${currentTVPage}.wiKi/`;
        log(`📺 TV 사이트 새로 고침: https://tv.${currentTVPage}.wiKi/`);
    }

    // 필터 테이블 함수
    function filterTable(keyword) {
        const tbody = document.getElementById('crawlTableBody');
        const rows = tbody.getElementsByTagName('tr');
        for (let i = 0; i < rows.length; i++) {
            const cells = rows[i].getElementsByTagName('td');
            let match = false;
            for (let j = 0; j < cells.length; j++) {
                if (cells[j].innerText.toLowerCase().includes(keyword.toLowerCase())) {
                    match = true;
                    break;
                }
            }
            rows[i].style.display = match ? '' : 'none';
        }
    }

    // 콘텐츠 품질 평가 함수
    function evaluateContentQuality(text) {
        const wordCount = text.split(/\s+/).length;
        let score = wordCount * 10;
        score = score > 100 ? 100 : score;
        return score;
    }

    // 키워드 분류 함수
    function classifyKeywords(description) {
        const categories = {
            '기술': ['기술', '테크', 'software'],
            '건강': ['건강', '헬스', '의학'],
            '스포츠': ['스포츠', '축구', '야구'],
            '엔터테인먼트': ['영화', '음악', '드라마'],
            '비즈니스': ['비즈니스', '경제', '주식']
        };
        for (let category in categories) {
            for (let keyword of categories[category]) {
                if (description.includes(keyword)) {
                    return category;
                }
            }
        }
        return '기타';
    }

    // 광고 제외 필터링 함수 (필요 시 확장 가능)
    function isAd(resultElement) {
        // 광고로 간주되는 특정 클래스나 속성 체크
        const adClasses = ['ad', 'ads', 'commercial', 'sponsored', 'promo'];
        return adClasses.some(cls => resultElement.classList.contains(cls));
    }

    updateUI();

})();                  진행바를 더 자세하게 해줘. 코드만 써. UI 개선은 필수임.