// ==UserScript==
// @name         이걸로 내 소원 사이트 찾으셈
// @namespace    http://tampermonkey.net/
// @version      3.11
// @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',
            queryParam: 'q',
            additionalParams: 'num=100&newwindow=1&tbs='
        },
        'www.bing.com': {
            name: 'Bing',
            resultSelector: 'li.b_algo',
            titleSelector: 'h2',
            linkSelector: 'a',
            descriptionSelector: 'p',
            nextButtonSelector: 'a.sb_pagN',
            queryParam: 'q',
            additionalParams: 'count=100&setlang=ko-kr&tbs='
        },
        'duckduckgo.com': {
            name: 'DuckDuckGo',
            resultSelector: 'div.result',
            titleSelector: 'h2 > a.result__a',
            linkSelector: 'a.result__a',
            descriptionSelector: '.result__snippet',
            nextButtonSelector: 'a.result--more__btn',
            queryParam: 'q',
            additionalParams: 'kl=ko-ko&tbs='
        },
        'search.yahoo.com': {
            name: 'Yahoo',
            resultSelector: 'div.dd.algo',
            titleSelector: 'h3.title',
            linkSelector: 'a',
            descriptionSelector: 'div.compText a.abstr',
            nextButtonSelector: 'a.next',
            queryParam: 'p',
            additionalParams: 'n=100&newwindow=1&tbs='
        },
        'www.baidu.com': {
            name: 'Baidu',
            resultSelector: 'div.result',
            titleSelector: 'h3 a',
            linkSelector: 'h3 a',
            descriptionSelector: 'div.c-abstract',
            nextButtonSelector: 'a.n',
            queryParam: 'wd',
            additionalParams: 'rn=100&tbs='
        },
        'www.ecosia.org': {
            name: 'Ecosia',
            resultSelector: 'div.result',
            titleSelector: 'h2 a',
            linkSelector: 'h2 a',
            descriptionSelector: 'div.abstract',
            nextButtonSelector: 'a.next',
            queryParam: 'q',
            additionalParams: 'num=100&newwindow=1&tbs='
        },
        'www.yandex.com': {
            name: 'Yandex',
            resultSelector: 'li.serp-item',
            titleSelector: 'h2 a',
            linkSelector: 'h2 a',
            descriptionSelector: 'div.text-container',
            nextButtonSelector: 'a.Link_theme_outer',
            queryParam: 'text',
            additionalParams: 'num=100&tbs='
        },
        'ask.com': {
            name: 'Ask',
            resultSelector: 'div.js-results-list div.Result',
            titleSelector: 'h2 a',
            linkSelector: 'h2 a',
            descriptionSelector: 'p.partial',
            nextButtonSelector: 'a.Next',
            queryParam: 'q',
            additionalParams: 'count=100&newwindow=1&tbs='
        },
        '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',
            queryParam: 'q',
            additionalParams: 'count=100&newwindow=1&tbs='
        },
        '뉴토끼.site': {
            name: '뉴토끼',
            resultSelector: '.search-result',
            titleSelector: '.result-title > a',
            linkSelector: '.result-title > a',
            descriptionSelector: '.result-description',
            nextButtonSelector: '.pagination-next',
            queryParam: 'q',
            additionalParams: 'num=100&newwindow=1&tbs='
        },
        'lettercounter.net': {
            name: 'LetterCounter',
            resultSelector: '.search-results .result-item',
            titleSelector: '.result-title > a',
            linkSelector: '.result-title > a',
            descriptionSelector: '.result-snippet',
            nextButtonSelector: '.pagination-next',
            queryParam: 'q',
            additionalParams: 'num=100&newwindow=1&tbs='
        },
        'tv.54.wiKi': {
            name: 'TV54',
            resultSelector: '.tv-search-result',
            titleSelector: '.tv-result-title > a',
            linkSelector: '.tv-result-title > a',
            descriptionSelector: '.tv-result-description',
            nextButtonSelector: '.tv-pagination-next',
            queryParam: 'q',
            additionalParams: 'num=100&newwindow=1&tbs='
        },
        'startpage.com': {
            name: 'Startpage',
            resultSelector: 'div.search_result',
            titleSelector: 'h3 a',
            linkSelector: 'h3 a',
            descriptionSelector: 'div.caption',
            nextButtonSelector: 'a.next',
            queryParam: 'query',
            additionalParams: 'num=100&newwindow=1&tbs='
        },
        'www.wolframalpha.com': {
            name: 'WolframAlpha',
            resultSelector: 'div.search-result',
            titleSelector: 'h2 a',
            linkSelector: 'h2 a',
            descriptionSelector: 'div.snippet',
            nextButtonSelector: 'a.next',
            queryParam: 'i',
            additionalParams: 'num=100&newwindow=1&tbs='
        }
    };

    const currentHost = window.location.host;
    const engine = searchEngines[currentHost];
    if (!engine) return;

    // 초기 설정
    const defaultSettings = {
        isCrawling: false,
        crawlData: [],
        currentPage: 1,
        logData: [],
        tvPage: 54,
        darkMode: false,
        currentFilter: '',
        blacklist: [],
        whitelist: [],
        crawlStartTime: null,
        searchQueue: []
    };

    for (let key in defaultSettings) {
        if (GM_getValue(key) === undefined) {
            GM_setValue(key, key === 'crawlStartTime' ? null : JSON.stringify(defaultSettings[key]));
        }
    }

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

    // Detect manual search
    let lastQuery = GM_getValue('lastQuery') || '';
    observeSearch();

    function observeSearch() {
        const observer = new MutationObserver(() => {
            const params = new URLSearchParams(window.location.search);
            const query = params.get(engine.queryParam);
            if (query && query !== lastQuery) {
                lastQuery = query;
                GM_setValue('lastQuery', lastQuery);
                GM_setValue('searchQueue', JSON.stringify([...JSON.parse(GM_getValue('searchQueue')), query]));
                log(`🔍 수동 검색어 감지 및 예약 추가: "${query}"`);
                if (!JSON.parse(GM_getValue('isCrawling'))) {
                    startCrawling();
                }
            }
        });

        observer.observe(document.body, { childList: true, subtree: true });
    }

    // UI 업데이트
    function updateUI() {
        const isCrawling = JSON.parse(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('currentTVPageIndicator').innerText = `📺 현재 TV 페이지: ${JSON.parse(GM_getValue('tvPage'))}`;
        document.getElementById('darkModeToggle').checked = JSON.parse(GM_getValue('darkMode'));
        document.getElementById('estimatedTime').innerText = getEstimatedFinishTime();
        updateScheduledSearches();
    }

    // UI 생성 함수
    function createUI() {
        const container = document.createElement('div');
        container.id = 'crawlUI';
        container.style.position = 'fixed';
        container.style.top = '10px';
        container.style.right = '10px';
        container.style.padding = '15px';
        container.style.backgroundColor = '#FFFFFF';
        container.style.border = '1px solid #DDD';
        container.style.borderRadius = '8px';
        container.style.boxShadow = '0 2px 12px rgba(0,0,0,0.3)';
        container.style.zIndex = '10000';
        container.style.width = '420px';
        container.style.fontFamily = 'Arial, 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.dark-mode {
                background-color: #2E2E2E !important;
                color: #FFFFFF !important;
                border: 1px solid #555 !important;
            }
            #crawlUI h2 {
                margin: 0 0 10px 0;
                font-size: 20px;
                color: #3388ff;
                text-align: center;
                display: flex;
                align-items: center;
                justify-content: center;
            }
            #crawlUI h2 svg {
                margin-right: 6px;
            }
            #crawlUI button {
                cursor: pointer;
                transition: background-color 0.3s;
                border: none;
                border-radius: 4px;
                padding: 8px;
                color: #fff;
                margin: 5px 0;
                width: 100%;
                font-size: 14px;
            }
            #crawlUI button:disabled {
                cursor: not-allowed;
                opacity: 0.6;
            }
            #crawlUI button.start {
                background-color: #28a745;
            }
            #crawlUI button.start:hover:not(:disabled) {
                background-color: #218838;
            }
            #crawlUI button.stop {
                background-color: #dc3545;
            }
            #crawlUI button.stop:hover:not(:disabled) {
                background-color: #c82333;
            }
            #crawlUI button.reset {
                background-color: #6c757d;
            }
            #crawlUI button.reset:hover {
                background-color: #5a6268;
            }
            #crawlUI button.refresh-tv {
                background-color: #17a2b8;
            }
            #crawlUI button.refresh-tv:hover {
                background-color: #138496;
            }
            #crawlUI label {
                display: block;
                margin-bottom: 5px;
                font-weight: bold;
            }
            #crawlUI input[type="text"] {
                width: 100%;
                padding: 6px;
                margin-bottom: 10px;
                border: 1px solid #CCC;
                border-radius: 4px;
                box-sizing: border-box;
                font-size: 14px;
            }
            #crawlUI textarea {
                width: 100%;
                height: 100px;
                padding: 6px;
                border: 1px solid #CCC;
                border-radius: 4px;
                box-sizing: border-box;
                font-size: 14px;
                resize: none;
                background-color: #f9f9f9;
                color: #333;
            }
            #crawlUI.dark-mode textarea {
                background-color: #3E3E3E !important;
                color: #FFFFFF !important;
                border: 1px solid #555 !important;
            }
            #crawlUI table {
                width: 100%;
                border-collapse: collapse;
                margin-bottom: 10px;
            }
            #crawlUI th, #crawlUI td {
                border: 1px solid #ddd;
                padding: 8px;
                text-align: left;
                font-size: 12px;
            }
            #crawlUI th {
                background-color: #3388ff;
                color: #fff;
            }
            #crawlUI .scheduled-search-item {
                display: flex;
                justify-content: space-between;
                align-items: center;
                padding: 4px 0;
                border-bottom: 1px solid #ddd;
            }
            #crawlUI .scheduled-search-item:last-child {
                border-bottom: none;
            }
            #crawlUI .remove-scheduled-search {
                background-color: #dc3545;
                border: none;
                border-radius: 4px;
                color: #fff;
                padding: 4px 8px;
                cursor: pointer;
                font-size: 12px;
            }
            #crawlUI .remove-scheduled-search:hover {
                background-color: #c82333;
            }
            #crawlUI .toggle-section {
                display: none;
            }
            #crawlUI .toggle-section.visible {
                display: block;
            }
        `;
        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="#3388ff">
                <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 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8
                8-8 8 3.59 8 8-3.59 8-8 8zm-1-13h2v6h-2zm0 8h2v2h-2z"/>
            </svg>
            🔍 크롤러
        `;
        container.appendChild(title);

        // 설정 섹션
        const settingsTab = document.createElement('button');
        settingsTab.innerText = '⚙️ 설정 토글';
        settingsTab.onclick = () => {
            settingsContent.classList.toggle('visible');
        };
        container.appendChild(settingsTab);

        const settingsContent = document.createElement('div');
        settingsContent.classList.add('toggle-section');

        // 화이트리스트
        const whitelistLabel = document.createElement('label');
        whitelistLabel.innerText = '✅ 화이트리스트 도메인 (콤마로 구분)';
        settingsContent.appendChild(whitelistLabel);
        const whitelistInput = document.createElement('input');
        whitelistInput.type = 'text';
        whitelistInput.id = 'whitelistInput';
        whitelistInput.value = 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);
            GM_setValue('whitelist', JSON.stringify(domains));
            log(`✅ 화이트리스트 업데이트: ${domains.join(', ')}`);
        };
        settingsContent.appendChild(whitelistInput);

        // 블랙리스트
        const blacklistLabel = document.createElement('label');
        blacklistLabel.innerText = '🚫 블랙리스트 도메인 (콤마로 구분)';
        settingsContent.appendChild(blacklistLabel);
        const blacklistInput = document.createElement('input');
        blacklistInput.type = 'text';
        blacklistInput.id = 'blacklistInput';
        blacklistInput.value = 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);
            GM_setValue('blacklist', JSON.stringify(domains));
            log(`🚫 블랙리스트 업데이트: ${domains.join(', ')}`);
        };
        settingsContent.appendChild(blacklistInput);

        container.appendChild(settingsContent);

        // 다크 모드
        const darkModeLabel = document.createElement('label');
        darkModeLabel.innerText = '🌙 다크 모드';
        darkModeLabel.setAttribute('for', 'darkModeToggle');
        container.appendChild(darkModeLabel);
        const darkModeToggle = document.createElement('input');
        darkModeToggle.type = 'checkbox';
        darkModeToggle.id = 'darkModeToggle';
        darkModeToggle.checked = JSON.parse(GM_getValue('darkMode'));
        darkModeToggle.onchange = () => {
            GM_setValue('darkMode', JSON.stringify(darkModeToggle.checked));
            toggleDarkMode(darkModeToggle.checked);
            log(`🌙 다크 모드 ${darkModeToggle.checked ? '활성화됨' : '비활성화됨'}`);
        };
        container.appendChild(darkModeToggle);

        // 크롤링 필터
        const filterLabel = document.createElement('label');
        filterLabel.innerText = '🔍 테이블 검색 필터';
        container.appendChild(filterLabel);
        const filterInput = document.createElement('input');
        filterInput.type = 'text';
        filterInput.id = 'filterInput';
        filterInput.value = JSON.parse(GM_getValue('currentFilter'));
        filterInput.placeholder = '키워드를 입력하세요...';
        filterInput.oninput = () => {
            const keyword = filterInput.value.trim();
            GM_setValue('currentFilter', JSON.stringify(keyword));
            filterTable(keyword);
        };
        container.appendChild(filterInput);

        // 예약 검색어 섹션
        const scheduledContainer = document.createElement('div');

        const scheduledTitle = document.createElement('label');
        scheduledTitle.innerText = '📅 예약 검색어';
        scheduledContainer.appendChild(scheduledTitle);

        const scheduledInput = document.createElement('input');
        scheduledInput.type = 'text';
        scheduledInput.id = 'addScheduledSearchInput';
        scheduledInput.placeholder = '검색어를 입력하세요...';
        scheduledContainer.appendChild(scheduledInput);

        const addButton = document.createElement('button');
        addButton.innerText = '➕ 추가';
        addButton.classList.add('start');
        addButton.onclick = addScheduledSearch;
        scheduledContainer.appendChild(addButton);

        const scheduledList = document.createElement('div');
        scheduledList.id = 'scheduledSearchesList';
        scheduledList.style.maxHeight = '100px';
        scheduledList.style.overflowY = 'auto';
        scheduledContainer.appendChild(scheduledList);

        container.appendChild(scheduledContainer);

        // 크롤링 컨트롤 버튼
        const startButton = document.createElement('button');
        startButton.innerText = '▶️ 시작';
        startButton.classList.add('start');
        startButton.id = 'startButton';
        startButton.onclick = startCrawling;
        container.appendChild(startButton);

        const stopButton = document.createElement('button');
        stopButton.innerText = '⏹️ 중지';
        stopButton.classList.add('stop');
        stopButton.id = 'stopButton';
        stopButton.onclick = stopCrawling;
        container.appendChild(stopButton);

        const resetButton = document.createElement('button');
        resetButton.innerText = '🔄 초기화';
        resetButton.classList.add('reset');
        resetButton.onclick = resetCrawling;
        container.appendChild(resetButton);

        // 진행률 표시
        const progressLabel = document.createElement('label');
        progressLabel.innerText = '📊 진행률';
        container.appendChild(progressLabel);
        const progressBar = document.createElement('progress');
        progressBar.id = 'progressBar';
        progressBar.value = getProgressPercentage();
        progressBar.max = 100;
        container.appendChild(progressBar);
        const progressText = document.createElement('span');
        progressText.id = 'progressText';
        progressText.innerText = `${getProgressPercentage()}% 완료`;
        container.appendChild(progressText);

        // 예상 완료 시간
        const estimatedTime = document.createElement('div');
        estimatedTime.id = 'estimatedTime';
        estimatedTime.innerText = '⏳ 예상 완료 시간: 계산 중...';
        container.appendChild(estimatedTime);

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

        // 로그 창
        const logLabel = document.createElement('label');
        logLabel.innerText = '📝 로그';
        container.appendChild(logLabel);
        const logArea = document.createElement('textarea');
        logArea.id = 'crawlLog';
        logArea.disabled = true;
        container.appendChild(logArea);

        // 결과 테이블
        const table = document.createElement('table');
        const thead = document.createElement('thead');
        const headerRow = document.createElement('tr');
        ['📰 사이트 이름', '🔗 주소', '📝 설명', '🗨️ 품질', '🗂️ 분류', '📄 보기'].forEach(text => {
            const th = document.createElement('th');
            th.innerText = text;
            thead.appendChild(th);
        });
        table.appendChild(thead);
        const tbody = document.createElement('tbody');
        tbody.id = 'crawlTableBody';
        table.appendChild(tbody);
        container.appendChild(table);

        // HTML 뷰어
        const htmlViewerContainer = document.createElement('div');
        htmlViewerContainer.id = 'htmlViewerContainer';
        htmlViewerContainer.style.display = 'none';
        const htmlViewerTitle = document.createElement('h3');
        htmlViewerTitle.innerText = '📄 HTML 뷰어';
        htmlViewerContainer.appendChild(htmlViewerTitle);
        const closeViewerButton = document.createElement('button');
        closeViewerButton.innerText = '❌ 닫기';
        closeViewerButton.classList.add('stop');
        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';
        htmlViewerContainer.appendChild(htmlViewerContent);
        container.appendChild(htmlViewerContainer);

        // 애니메이션 사이트 뷰어
        const animeViewerContainer = document.createElement('div');
        animeViewerContainer.id = 'animeViewerContainer';
        const animeViewerTitle = document.createElement('h3');
        animeViewerTitle.innerText = '🎬 애니메이션 보기';
        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';
        animeViewerContainer.appendChild(animeViewerFrame);
        container.appendChild(animeViewerContainer);

        // TV 사이트 뷰어
        const tvViewerContainer = document.createElement('div');
        tvViewerContainer.id = 'tvViewerContainer';
        const tvViewerTitle = document.createElement('h3');
        tvViewerTitle.innerText = '📺 TV 사이트 보기';
        tvViewerContainer.appendChild(tvViewerTitle);
        const tvViewerFrame = document.createElement('iframe');
        tvViewerFrame.id = 'tvViewerFrame';
        tvViewerFrame.src = `https://tv.${JSON.parse(GM_getValue('tvPage'))}.wiKi/`;
        tvViewerFrame.style.width = '100%';
        tvViewerFrame.style.height = '300px';
        tvViewerFrame.style.border = '1px solid #CCC';
        tvViewerContainer.appendChild(tvViewerFrame);
        const refreshTVButton = document.createElement('button');
        refreshTVButton.innerText = '🔄 TV 사이트 새로 고침';
        refreshTVButton.classList.add('refresh-tv');
        refreshTVButton.onclick = refreshTVSite;
        tvViewerContainer.appendChild(refreshTVButton);
        container.appendChild(tvViewerContainer);

        document.body.appendChild(container);

        toggleDarkMode(JSON.parse(GM_getValue('darkMode')));
        loadCrawlData();
        loadLogData();
        loadFilterKeywords();
        updateScheduledSearches();
    }

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

    // 크롤링 시작
    function startCrawling() {
        if (JSON.parse(GM_getValue('isCrawling'))) return;
        const searchQueue = JSON.parse(GM_getValue('searchQueue'));
        if (searchQueue.length === 0) {
            alert('예약된 검색어가 없습니다!');
            return;
        }
        GM_setValue('isCrawling', JSON.stringify(true));
        GM_setValue('crawlStartTime', JSON.stringify(Date.now()));
        log('🔍 크롤링 시작');
        processNextSearch();
    }

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

    // 크롤링 초기화
    function resetCrawling() {
        stopCrawling();
        GM_setValue('crawlData', JSON.stringify([]));
        GM_setValue('currentPage', JSON.stringify(1));
        GM_setValue('tvPage', JSON.stringify(54));
        GM_setValue('crawlStartTime', JSON.stringify(null));
        GM_setValue('searchQueue', JSON.stringify([]));
        clearTable();
        clearLog();
        log('🔄 크롤링 초기화 완료');
        updateProgressDetails();
        document.getElementById('filterInput').value = '';
        GM_setValue('currentFilter', JSON.stringify(''));
        filterTable('');
        updateScheduledSearches();
    }

    // 테이블 초기화
    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));
    }

    // 로그 데이터 로드
    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() {
        const filter = JSON.parse(GM_getValue('currentFilter'));
        filterTable(filter);
    }

    // 로그 추가
    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() {
        const totalPages = 100;
        const currentPage = parseInt(JSON.parse(GM_getValue('currentPage')));
        return Math.min(Math.floor((currentPage / totalPages) * 100), 100);
    }

    // 상세 진행률 업데이트
    function updateProgressDetails() {
        const progressDetails = document.getElementById('estimatedTime');
        const crawlStart = parseInt(JSON.parse(GM_getValue('crawlStartTime')));
        if (!crawlStart) {
            progressDetails.innerText = '⏳ 예상 완료 시간: 아직 시작되지 않음';
            return;
        }
        const totalPages = 100;
        const currentPage = parseInt(JSON.parse(GM_getValue('currentPage')));
        const pagesLeft = totalPages - currentPage;
        const elapsedTime = Date.now() - crawlStart;
        const rate = elapsedTime / currentPage;
        const estimatedRemaining = rate * pagesLeft;
        const estimatedFinish = new Date(Date.now() + estimatedRemaining);
        progressDetails.innerText = `⏳ 예상 완료 시간: ${estimatedFinish.toLocaleTimeString()}`;
    }

    // 예약 검색어 추가
    function addScheduledSearch() {
        const input = document.getElementById('addScheduledSearchInput');
        const query = input.value.trim();
        if (!query) {
            alert('검색어를 입력하세요!');
            return;
        }
        let queue = JSON.parse(GM_getValue('searchQueue'));
        queue.push(query);
        GM_setValue('searchQueue', JSON.stringify(queue));
        log(`📅 예약된 검색어 추가: "${query}"`);
        input.value = '';
        updateScheduledSearches();
    }

    // 예약 검색어 목록 업데이트
    function updateScheduledSearches() {
        const list = document.getElementById('scheduledSearchesList');
        list.innerHTML = '';
        const queue = JSON.parse(GM_getValue('searchQueue'));
        queue.forEach((search, index) => {
            const item = document.createElement('div');
            item.classList.add('scheduled-search-item');

            const span = document.createElement('span');
            span.innerText = search;
            item.appendChild(span);

            const removeBtn = document.createElement('button');
            removeBtn.innerText = '❌';
            removeBtn.classList.add('remove-scheduled-search');
            removeBtn.onclick = () => removeScheduledSearch(index);
            item.appendChild(removeBtn);

            list.appendChild(item);
        });
    }

    // 예약 검색어 제거
    function removeScheduledSearch(index) {
        let queue = JSON.parse(GM_getValue('searchQueue'));
        const removed = queue.splice(index, 1);
        GM_setValue('searchQueue', JSON.stringify(queue));
        log(`📅 예약된 검색어 제거: "${removed[0]}"`);
        updateScheduledSearches();
    }

    // 크롤링 시작 시 다음 검색어 처리
    function processNextSearch() {
        if (!JSON.parse(GM_getValue('isCrawling'))) return;
        let queue = JSON.parse(GM_getValue('searchQueue'));
        if (queue.length === 0) {
            GM_setValue('isCrawling', JSON.stringify(false));
            GM_setValue('crawlStartTime', JSON.stringify(null));
            log('🏁 모든 크롤링 완료');
            alert('모든 예약된 크롤링이 완료되었습니다!');
            return;
        }
        const nextQuery = queue.shift();
        GM_setValue('searchQueue', JSON.stringify(queue));
        log(`📅 예약된 검색어 시작: "${nextQuery}"`);
        performSearch(nextQuery);
    }

    // 검색 수행 및 크롤링
    function performSearch(query) {
        const params = new URLSearchParams();
        params.set(engine.queryParam, query);
        if (engine.additionalParams) {
            const additional = engine.additionalParams.split('&').filter(param => !param.includes('tbs=')).join('&');
            additional.split('&').forEach(param => {
                const [key, value] = param.split('=');
                if (key && value) params.set(key, value);
            });
        }
        const searchUrl = `https://${currentHost}/search?${params.toString()}`;
        log(`🔍 검색 시작: "${query}"`);
        performCrawl(searchUrl);
    }

    // 크롤링 수행
    function performCrawl(url) {
        GM_xmlhttpRequest({
            method: 'GET',
            url: url,
            headers: {
                'User-Agent': navigator.userAgent,
                'Accept': 'text/html'
            },
            onload: function(response) {
                if (response.status >= 200 && response.status < 300) {
                    const parser = new DOMParser();
                    const doc = parser.parseFromString(response.responseText, 'text/html');
                    const results = doc.querySelectorAll(engine.resultSelector);
                    processResults(results);
                    const nextButton = doc.querySelector(engine.nextButtonSelector);
                    const currentPage = parseInt(JSON.parse(GM_getValue('currentPage')));
                    if (nextButton && currentPage < 100 && JSON.parse(GM_getValue('isCrawling'))) {
                        GM_setValue('currentPage', JSON.stringify(currentPage + 1));
                        log(`➡️ 다음 페이지로 이동: 페이지 ${currentPage + 1}`);
                        const nextPageUrl = nextButton.href;
                        setTimeout(() => {
                            performCrawl(nextPageUrl);
                        }, 1000); // 1초 지연
                    } else {
                        log('🏁 현재 검색어의 모든 페이지 크롤링 완료');
                        processNextSearch();
                    }
                } else {
                    log(`❌ 페이지 로드 실패 (${response.status}): ${url}`);
                    processNextSearch();
                }
            },
            onerror: function() {
                log(`⚠️ 페이지 요청 에러: ${url}`);
                processNextSearch();
            }
        });
    }

    // 결과 처리
    function processResults(results) {
        let crawlData = JSON.parse(GM_getValue('crawlData'));
        const blacklist = JSON.parse(GM_getValue('blacklist'));
        const whitelist = JSON.parse(GM_getValue('whitelist'));
        const currentFilter = JSON.parse(GM_getValue('currentFilter'));

        results.forEach(result => {
            const linkEl = result.querySelector(engine.linkSelector);
            const titleEl = result.querySelector(engine.titleSelector);
            const descEl = result.querySelector(engine.descriptionSelector);

            if (linkEl && titleEl) {
                try {
                    const url = new URL(linkEl.href);
                    const domain = url.hostname.replace('www.', '');
                    if (blacklist.includes(domain)) {
                        log(`🚫 블랙리스트 도메인 필터링: ${domain}`);
                        return;
                    }
                } catch {
                    log(`⚠️ 유효하지 않은 URL: ${linkEl.href}`);
                    return;
                }

                if (isAd(result)) {
                    return;
                }

                const title = titleEl.innerText.trim();
                const url = linkEl.href;
                const description = descEl ? descEl.innerText.trim() : '';

                // 필터링
                if (currentFilter) {
                    const keywords = currentFilter.split(',').map(k => k.trim()).filter(k => k);
                    const match = keywords.every(kw => {
                        const orKeywords = kw.split('|').map(k => k.trim()).filter(k => k);
                        return orKeywords.some(subKw => {
                            return title.toLowerCase().includes(subKw.toLowerCase()) || description.toLowerCase().includes(subKw.toLowerCase());
                        });
                    });
                    if (!match) {
                        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();
        updateProgressDetails();
    }

    // 테이블에 행 추가
    function addRowToTable(item) {
        const tbody = document.getElementById('crawlTableBody');
        if (!tbody) return;

        const row = document.createElement('tr');

        const titleCell = document.createElement('td');
        titleCell.innerText = item.title;
        titleCell.style.fontWeight = 'bold';
        row.appendChild(titleCell);

        const urlCell = document.createElement('td');
        const link = document.createElement('a');
        link.href = item.url;
        link.innerText = item.url;
        link.target = '_blank';
        link.style.color = '#3388ff';
        urlCell.appendChild(link);
        row.appendChild(urlCell);

        const descCell = document.createElement('td');
        descCell.innerText = item.description;
        descCell.style.color = '#555';
        row.appendChild(descCell);

        const qualityCell = document.createElement('td');
        qualityCell.innerText = item.quality;
        qualityCell.style.color = item.quality > 50 ? '#28a745' : '#dc3545';
        qualityCell.style.fontWeight = 'bold';
        row.appendChild(qualityCell);

        const classificationCell = document.createElement('td');
        classificationCell.innerText = item.classification;
        classificationCell.style.fontWeight = 'bold';
        row.appendChild(classificationCell);

        const viewButtonCell = document.createElement('td');
        const viewBtn = document.createElement('button');
        viewBtn.innerText = '📄 보기';
        viewBtn.style.backgroundColor = '#17a2b8';
        viewBtn.style.color = '#fff';
        viewBtn.style.border = 'none';
        viewBtn.style.borderRadius = '4px';
        viewBtn.style.cursor = 'pointer';
        viewBtn.onclick = () => viewHTML(item.url);
        viewButtonCell.appendChild(viewBtn);
        row.appendChild(viewButtonCell);

        tbody.appendChild(row);
    }

    // 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);
                    log(`✅ HTML 로드 완료: ${url}`);
                } else {
                    log(`❌ HTML 로드 실패 (${response.status}): ${url}`);
                    alert(`HTML 로드 실패: ${response.status} ${response.statusText}`);
                }
            },
            onerror: function() {
                log(`⚠️ HTML 요청 에러: ${url}`);
                alert(`HTML 요청 에러: ${url}`);
            }
        });
    }

    // HTML 디스플레이
    function displayHTML(htmlContent) {
        const viewer = document.getElementById('htmlViewerContainer');
        const iframe = document.getElementById('htmlViewerContent');
        const blob = new Blob([htmlContent], { type: 'text/html' });
        const url = URL.createObjectURL(blob);
        iframe.src = url;
        viewer.style.display = 'block';
    }

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

    // 테이블 필터링
    function filterTable(keyword) {
        const tbody = document.getElementById('crawlTableBody');
        const rows = tbody.getElementsByTagName('tr');
        for (let row of rows) {
            const cells = row.getElementsByTagName('td');
            let match = true;
            if (keyword) {
                const keywords = keyword.split(',').map(k => k.trim()).filter(k => k);
                match = keywords.every(kw => {
                    const orKeywords = kw.split('|').map(k => k.trim()).filter(k => k);
                    return orKeywords.some(subKw => {
                        return Array.from(cells).some(cell => cell.innerText.toLowerCase().includes(subKw.toLowerCase()));
                    });
                });
            }
            row.style.display = match ? '' : 'none';
        }
    }

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

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

    // 광고 필터링
    function isAd(element) {
        const adClasses = ['ad', 'ads', 'commercial', 'sponsored', 'promo'];
        return adClasses.some(cls => element.classList.contains(cls));
    }

    // 크롤링 상태 리스너
    GM_addValueChangeListener('isCrawling', () => {
        updateUI();
    });

    // 다크 모드 리스너
    GM_addValueChangeListener('darkMode', () => {
        const isDark = JSON.parse(GM_getValue('darkMode'));
        toggleDarkMode(isDark);
    });

    // 필터 키워드 리스너
    GM_addValueChangeListener('currentFilter', () => {
        loadFilterKeywords();
    });

    // 화이트리스트 리스너
    GM_addValueChangeListener('whitelist', () => {
        log(`✅ 화이트리스트 업데이트: ${JSON.parse(GM_getValue('whitelist')).join(', ')}`);
    });

    // 블랙리스트 리스너
    GM_addValueChangeListener('blacklist', () => {
        log(`🚫 블랙리스트 업데이트: ${JSON.parse(GM_getValue('blacklist')).join(', ')}`);
    });

    // 검색 큐 리스너
    GM_addValueChangeListener('searchQueue', () => {
        updateScheduledSearches();
    });

    // 크롤링 상태 변경 시 자동 다음 검색어 처리
    GM_addValueChangeListener('isCrawling', (name, oldValue, newValue) => {
        if (JSON.parse(newValue)) {
            processNextSearch();
        }
    });

    updateUI();
    updateProgressDetails();

    // 크롤링 함수 수정: 백그라운드 크롤링으로 변경하여 페이지 리로드 방지
    function crawlPage() {
        // 기능 제거: 페이지 리로드 없이 백그라운드 크롤링으로 처리
    }

})();                                        내가 검색창에 검색한 걸 마지막 페이지까지 갔다가 만약에 예약된게 있다면 이걸 바탕으로 다시 하고 이런 구조 만들어 ㄱㄱ + 애니 사이트 애니 다운로드 기능 추가 (네이버 블로그 영상 다운로드 참고해서 만드셈 ) + 코드만 쓰셈  코드만 쓰셈!!!!!!!!!!!!!!!!!!!!