// ==UserScript==
// @name         Advanced Web Crawler Enhanced Pro
// @namespace    http://tampermonkey.net/
// @version      6.1
// @description  고급 웹 크롤러 스크립트 with Enhanced Settings, Improved Accuracy, Resizable UI, and Additional Features
// @author       Your Name
// @match        https://www.google.com/search*
// @match        https://www.bing.com/search*
// @match        https://search.yahoo.com/search*
// @match        https://duckduckgo.com/*
// @match        https://www.baidu.com/s*
// @match        https://www.yandex.com/search/*
// @match        https://search.naver.com/search*
// @match        https://search.daum.net/search*
// @match        https://www.ask.com/*
// @match        https://www.ecosia.org/search*
// @match        https://www.startpage.com/*
// @match        https://searx.me/*
// @match        https://www.dogpile.com/*
// @match        https://www.bing.com/images*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @connect      *
// @require      https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js
// @require      https://cdn.jsdelivr.net/npm/chart.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js
// ==/UserScript==

(function() {
    'use strict';

    // 기본 설정
    const defaultSettings = {
        // ... 기존 설정 유지 ...
        // (생략: 이전 스크립트의 defaultSettings를 그대로 유지)
        // 추가 설정 항목 정의
        savedSites: [] // 저장된 사이트 리스트
    };

    // 현재 설정 로드
    const settings = {
        // ... 기존 설정 로드 ...
        // (생략: 이전 스크립트의 settings 로드 부분을 유지)
        savedSites: GM_getValue('savedSites', [])
    };

    // 크롤링 상태 복원
    let visitedUrls = GM_getValue('visitedUrls', []);
    let logs = GM_getValue('logs', []);
    let bugLogs = GM_getValue('bugLogs', []); // 버그 로그 추가
    let data = GM_getValue('data', []);
    let isCrawling = GM_getValue('isCrawling', false);
    let currentPage = GM_getValue('currentPage', 1);
    let totalPages = GM_getValue('totalPages', settings.maxPages);
    let progress = GM_getValue('progress', 0);
    let currentDepth = GM_getValue('currentDepth', 0);
    let activeRequests = 0;
    let requestQueue = [];

    // UI 생성
    function createUI() {
        const style = `
            /* 기존 스타일 유지 */
            /* ... (생략) ... */
            /* 로그 섹션 추가 스타일 */
            #log-messages { height: 200px; overflow-y: auto; background: #f9f9f9; padding: 10px; border: 1px solid #ccc; border-radius: 4px; }
            #bug-log-messages { height: 200px; overflow-y: auto; background: #ffe6e6; padding: 10px; border: 1px solid #ffcccc; border-radius: 4px; }
            /* 저장된 사이트 섹션 스타일 */
            #saved-sites {
                margin-top: 20px;
                border: 1px solid #ccc;
                padding: 10px;
                border-radius: 4px;
                background: #f9f9f9;
            }
            #saved-sites table {
                width: 100%;
                border-collapse: collapse;
            }
            #saved-sites th, #saved-sites td {
                padding: 8px;
                text-align: left;
                border-bottom: 1px solid #ddd;
            }
            #saved-sites th {
                background-color: #f2f2f2;
            }
            #saved-sites button {
                padding: 4px 8px;
                margin: 2px;
                border: none;
                background-color: #f44336;
                color: white;
                border-radius: 4px;
                cursor: pointer;
            }
            #saved-sites button:hover {
                background-color: #d32f2f;
            }
        `;
        $('head').append(`<style>${style}</style>`);

        const container = $(`
            <div id="crawler-container">
                <div id="crawler-tabs">
                    <div class="crawler-tab ${currentTab() === 'logs' ? 'active' : ''}" data-tab="logs" title="로그 보기">📜 로그</div>
                    <div class="crawler-tab ${currentTab() === 'bugLogs' ? 'active' : ''}" data-tab="bugLogs" title="버그 로그 보기">🐞 버그 로그</div>
                    <div class="crawler-tab ${currentTab() === 'data' ? 'active' : ''}" data-tab="data" title="수집된 데이터 보기">📊 데이터</div>
                    <div class="crawler-tab ${currentTab() === 'settings' ? 'active' : ''}" data-tab="settings" title="설정 변경">⚙️ 설정</div>
                    <div class="crawler-tab ${currentTab() === 'savedSites' ? 'active' : ''}" data-tab="savedSites" title="저장된 사이트 관리">💾 저장된 사이트</div>
                </div>
                <div id="crawler-content">
                    <div id="tab-logs" style="display: ${currentTab() === 'logs' ? 'block' : 'none'};">
                        <div class="progress-bar">
                            <div class="progress-bar-fill" style="width: ${progress.toFixed(2)}%;"></div>
                        </div>
                        <div class="status-text">진행률: ${progress.toFixed(2)}% (${currentPage}/${totalPages} 페이지)</div>
                        <div class="control-buttons">
                            <button id="start-crawling" title="크롤링을 시작합니다." ${isCrawling ? 'disabled' : ''}>▶ 시작</button>
                            <button id="stop-crawling" title="크롤링을 중지합니다." ${isCrawling ? '' : 'disabled'}>■ 중지</button>
                            ${settings.exportFormats.csv ? `<button id="download-csv" title="수집된 데이터를 CSV 파일로 다운로드합니다.">📥 CSV</button>` : ''}
                            ${settings.exportFormats.json ? `<button id="download-json" title="수집된 데이터를 JSON 파일로 다운로드합니다.">📥 JSON</button>` : ''}
                            ${settings.exportFormats.xml ? `<button id="download-xml" title="수집된 데이터를 XML 파일로 다운로드합니다.">📥 XML</button>` : ''}
                            ${settings.exportFormats.xls ? `<button id="download-xls" title="수집된 데이터를 XLS 파일로 다운로드합니다.">📥 XLS</button>` : ''}
                        </div>
                        <div id="log-messages">로그 내용이 여기에 표시됩니다.</div>
                    </div>
                    <div id="tab-bugLogs" style="display: ${currentTab() === 'bugLogs' ? 'block' : 'none'};">
                        <div id="bug-log-messages">버그 로그가 여기에 표시됩니다.</div>
                    </div>
                    <div id="tab-data" style="display: ${currentTab() === 'data' ? 'block' : 'none'};">
                        <input type="text" id="search-filter" placeholder="검색 필터..." title="데이터 필터링을 위한 키워드를 입력하세요." style="width: 100%; padding: 5px; margin-bottom: 10px; border: 1px solid #ccc; border-radius: 4px;">
                        <table>
                            <thead>
                                <tr>
                                    ${settings.dataFields.name ? '<th data-sort="name">사이트 이름</th>' : ''}
                                    ${settings.dataFields.url ? '<th data-sort="url">주소</th>' : ''}
                                    ${settings.dataFields.description ? '<th data-sort="description">설명</th>' : ''}
                                    ${settings.dataFields.html ? '<th>📄 HTML 내용</th>' : ''}
                                    ${settings.dataFields.category ? '<th data-sort="category">분류</th>' : ''}
                                    ${settings.dataFields.quality ? '<th data-sort="quality">품질점수</th>' : ''}
                                    ${settings.dataFields.language ? '<th data-sort="language">언어</th>' : ''}
                                    ${settings.dataFields.wordCount ? '<th data-sort="wordCount">단어 수</th>' : ''}
                                    ${settings.dataFields.metaTags ? '<th>메타 태그</th>' : ''}
                                    ${settings.dataFields.headings ? '<th>헤딩</th>' : ''}
                                </tr>
                            </thead>
                            <tbody id="data-table-body">
                                <!-- 데이터 행이 여기에 추가됩니다. -->
                            </tbody>
                        </table>
                        <!-- 데이터 시각화 섹션 -->
                        ${settings.dataVisualization ? `
                            <div id="data-visualization">
                                <h3>데이터 시각화</h3>
                                <canvas id="data-chart"></canvas>
                            </div>
                        ` : ''}
                    </div>
                    <div id="tab-settings" style="display: ${currentTab() === 'settings' ? 'block' : 'none'};">
                        <!-- 기존 설정 탭 내용 유지 -->
                        <!-- ... (생략) ... -->
                    </div>
                    <div id="tab-savedSites" style="display: ${currentTab() === 'savedSites' ? 'block' : 'none'};">
                        <h3>저장된 사이트 관리</h3>
                        <input type="text" id="new-saved-site" placeholder="추가할 사이트 URL" title="저장할 사이트의 URL을 입력하세요." style="width: 80%; padding: 5px; margin-bottom: 10px; border: 1px solid #ccc; border-radius: 4px;">
                        <button id="add-saved-site" title="새 사이트를 저장합니다." style="padding: 5px 10px; border: none; background-color: #4caf50; color: white; border-radius: 4px; cursor: pointer;">추가</button>
                        <table>
                            <thead>
                                <tr>
                                    <th>저장된 사이트 URL</th>
                                    <th>관리</th>
                                </tr>
                            </thead>
                            <tbody id="saved-sites-table-body">
                                <!-- 저장된 사이트가 여기에 표시됩니다. -->
                            </tbody>
                        </table>
                    </div>
                </div>

                <!-- 모달 창 -->
                <div id="html-modal" class="modal">
                    <div class="modal-content">
                        <span class="close">&times;</span>
                        <pre id="modal-html-content"></pre>
                    </div>
                </div>
            </div>
        `);
        $('body').append(container);

        // 탭 전환 이벤트
        $('.crawler-tab').on('click', function() {
            const tab = $(this).data('tab');
            $('.crawler-tab').removeClass('active');
            $(this).addClass('active');
            $('#crawler-content > div').hide();
            $(`#tab-${tab}`).show();
            saveCurrentTab(tab);
        });

        // 단축키 이벤트
        $(document).on('keydown', function(e) {
            if (e.ctrlKey) {
                switch (e.key) {
                    case '1':
                        $('.crawler-tab[data-tab="logs"]').click();
                        break;
                    case '2':
                        $('.crawler-tab[data-tab="bugLogs"]').click();
                        break;
                    case '3':
                        $('.crawler-tab[data-tab="data"]').click();
                        break;
                    case '4':
                        $('.crawler-tab[data-tab="settings"]').click();
                        break;
                    case '5':
                        $('.crawler-tab[data-tab="savedSites"]').click();
                        break;
                    case 's':
                        e.preventDefault();
                        saveSettings();
                        break;
                    case 'p':
                        e.preventDefault();
                        if (!isCrawling) startCrawling();
                        break;
                    case 'q':
                        e.preventDefault();
                        if (isCrawling) stopCrawling();
                        break;
                    case 'd':
                        e.preventDefault();
                        downloadCSV();
                        break;
                    case 'j':
                        e.preventDefault();
                        downloadJSON();
                        break;
                    case 'x':
                        e.preventDefault();
                        downloadXML();
                        break;
                    case 'l':
                        e.preventDefault();
                        downloadXLS();
                        break;
                }
            }
        });

        // 설정 저장 버튼 이벤트
        $('#save-settings').on('click', saveSettings);
        $('#reset-settings').on('click', resetSettings);

        // 추가/제거 사용자 에이전트 버튼 이벤트
        $('#add-user-agent').on('click', function() {
            const newUA = prompt("새 사용자 에이전트를 입력하세요:");
            if (newUA) {
                settings.userAgents.push(newUA);
                GM_setValue('userAgents', settings.userAgents);
                updateUserAgentSelect();
                log('새 사용자 에이전트가 추가되었습니다.', 'success');
            }
        });

        $('#remove-user-agent').on('click', function() {
            const selectedOptions = $('#user-agent-select').val();
            if (selectedOptions && selectedOptions.length > 0) {
                settings.userAgents = settings.userAgents.filter((_, idx) => !selectedOptions.includes(idx.toString()));
                GM_setValue('userAgents', settings.userAgents);
                settings.userAgentIndex = 0;
                GM_setValue('userAgentIndex', settings.userAgentIndex);
                updateUserAgentSelect();
                log('선택한 사용자 에이전트가 제거되었습니다.', 'success');
            } else {
                alert('제거할 사용자 에이전트를 선택하세요.');
            }
        });

        // 다운로드 버튼 이벤트
        $('#download-csv').on('click', downloadCSV);
        $('#download-json').on('click', downloadJSON);
        $('#download-xml').on('click', downloadXML);
        $('#download-xls').on('click', downloadXLS);

        // 검색 필터 기능
        $('#search-filter').on('input', function() {
            filterTable($(this).val());
        });

        // 스케줄링 옵션 표시/숨기기
        $('#schedule-enabled').on('change', function() {
            if ($(this).is(':checked')) {
                $('#schedule-options').slideDown();
            } else {
                $('#schedule-options').slideUp();
            }
        });

        // 데이터 시각화 토글
        $('#data-visualization-checkbox').on('change', function() {
            settings.dataVisualization = $(this).is(':checked');
            GM_setValue('dataVisualization', settings.dataVisualization);
            $('#data-visualization').toggle(settings.dataVisualization);
            log('데이터 시각화 설정이 변경되었습니다.', 'success');
            if (settings.dataVisualization) {
                renderChart();
            } else {
                $('#data-chart').remove();
            }
        });

        // CAPTCHA 처리 토글
        $('#enable-captchas-checkbox').on('change', function() {
            settings.enableCaptchas = $(this).is(':checked');
            GM_setValue('enableCaptchas', settings.enableCaptchas);
            log('CAPTCHA 처리 설정이 변경되었습니다.', 'success');
        });

        // 사용자 지정 키워드 분류 섹션 추가
        // 이미 textarea에 설정됨

        // 모달 창 기능
        const modal = document.getElementById("html-modal");
        const span = document.getElementsByClassName("close")[0];
        span.onclick = function() { modal.style.display = "none"; }
        window.onclick = function(event) {
            if (event.target == modal) {
                modal.style.display = "none";
            }
        }

        // 시작 및 중지 버튼 이벤트
        $('#start-crawling').on('click', startCrawling);
        $('#stop-crawling').on('click', stopCrawling);

        // export 버튼 추가 이벤트
        // XML 다운로드
        $('#download-xml').on('click', downloadXML);
        // XLS 다운로드
        $('#download-xls').on('click', downloadXLS);

        // 저장된 사이트 관리 이벤트
        $('#add-saved-site').on('click', function() {
            const newSite = $('#new-saved-site').val().trim();
            if (newSite && isValidURL(newSite)) {
                if (!settings.savedSites.includes(newSite)) {
                    settings.savedSites.push(newSite);
                    GM_setValue('savedSites', settings.savedSites);
                    $('#new-saved-site').val('');
                    updateSavedSitesUI();
                    log(`새로운 사이트가 저장되었습니다: ${newSite}`, 'success');
                } else {
                    alert('이미 저장된 사이트입니다.');
                }
            } else {
                alert('유효한 URL을 입력하세요.');
            }
        });

        $('#saved-sites-table-body').on('click', '.remove-saved-site', function() {
            const site = $(this).data('site');
            settings.savedSites = settings.savedSites.filter(s => s !== site);
            GM_setValue('savedSites', settings.savedSites);
            updateSavedSitesUI();
            log(`저장된 사이트가 제거되었습니다: ${site}`, 'success');
        });

        // 다운로드 버튼 추가 이벤트
        // XML 다운로드
        $('#download-xml').on('click', downloadXML);
        // XLS 다운로드
        $('#download-xls').on('click', downloadXLS);

        // 초기 UI 업데이트
        updateLogsUI();
        updateBugLogsUI();
        updateDataUI();
        updateSavedSitesUI();
        if (settings.dataVisualization) {
            renderChart();
        }
    }

    function currentTab() {
        return GM_getValue('currentTab', 'logs');
    }

    function saveCurrentTab(tab) {
        GM_setValue('currentTab', tab);
    }

    function updateUserAgentSelect() {
        $('#user-agent-select').empty();
        settings.userAgents.forEach((ua, idx) => {
            const selected = idx === settings.userAgentIndex ? 'selected' : '';
            $('#user-agent-select').append(`<option value="${idx}" ${selected}>${ua}</option>`);
        });
    }

    // 사용자 지정 키워드 텍스트 생성 함수
    function getCustomKeywordsText() {
        const customKeywords = settings.customKeywords || {};
        return Object.entries(customKeywords).map(([category, kws]) => `${category}:${kws.join(',')}`).join('\n');
    }

    // 사용자 지정 키워드 저장 및 로드 함수
    function parseCustomKeywords(text) {
        const lines = text.split('\n').map(line => line.trim()).filter(line => line);
        const customKeywords = {};
        lines.forEach(line => {
            const [category, kws] = line.split(':');
            if (category && kws) {
                customKeywords[category.trim()] = kws.split(',').map(kw => kw.trim()).filter(kw => kw);
            }
        });
        return customKeywords;
    }

    function combineKeywords() {
        const standardKeywords = {
            '교육': ['학교', '교육', '학생', '교수', '강의'],
            '기술': ['기술', '소프트웨어', '하드웨어', '프로그래밍', '개발'],
            '뉴스': ['뉴스', '속보', '기사', '보도'],
            '엔터테인먼트': ['영화', '음악', '연예', '게임', '드라마'],
            '스포츠': ['축구', '야구', '농구', '경기', '토너먼트'],
            '비즈니스': ['비즈니스', '경제', '주식', '투자', '시장'],
            '건강': ['건강', '의료', '헬스', '운동', '영양'],
            '과학': ['과학', '연구', '실험', '발견', '이론'],
            '문학': ['소설', '시', '문학', '작가', '책'],
            '여행': ['여행', '관광', '숙박', '비행기', '여권']
            // 추가 키워드 및 카테고리
        };
        const customKeywords = settings.customKeywords || {};

        for (const [category, kws] of Object.entries(customKeywords)) {
            if (standardKeywords[category]) {
                standardKeywords[category] = [...new Set([...standardKeywords[category], ...kws])];
            } else {
                standardKeywords[category] = kws;
            }
        }

        return standardKeywords;
    }

    // 로깅 함수
    function log(message, type = 'info') {
        const timestamp = new Date().toLocaleString();
        if (type === 'bug') {
            bugLogs.push({ timestamp, message, type });
            GM_setValue('bugLogs', bugLogs);
            updateBugLogsUI();
        } else {
            logs.push({ timestamp, message, type });
            GM_setValue('logs', logs);
            updateLogsUI();
        }

        if (settings.advancedLogging) {
            // type을 콘솔 메서드로 매핑
            switch(type) {
                case 'info':
                    console.info(`[${timestamp}] [INFO] ${message}`);
                    break;
                case 'warning':
                    console.warn(`[${timestamp}] [WARNING] ${message}`);
                    break;
                case 'error':
                    console.error(`[${timestamp}] [ERROR] ${message}`);
                    break;
                case 'success':
                    console.log(`%c[${timestamp}] [SUCCESS] ${message}`, 'color: green; font-weight: bold;');
                    break;
                case 'bug':
                    console.error(`%c[${timestamp}] [BUG] ${message}`, 'color: red; font-weight: bold;');
                    break;
                default:
                    console.log(`[${timestamp}] [${type.toUpperCase()}] ${message}`);
            }
        }
    }

    function updateLogsUI() {
        const logHtml = logs.map(log => `<div>[${log.timestamp}] [${log.type.toUpperCase()}] ${log.message}</div>`).join('');
        $('#log-messages').html(logHtml);
        $('.progress-bar-fill').css('width', `${progress.toFixed(2)}%`);
        $('.status-text').text(`진행률: ${progress.toFixed(2)}% (${currentPage}/${totalPages} 페이지)`);
    }

    function updateBugLogsUI() {
        const bugLogHtml = bugLogs.map(log => `<div style="color: red;">[${log.timestamp}] [${log.type.toUpperCase()}] ${log.message}</div>`).join('');
        $('#bug-log-messages').html(bugLogHtml);
    }

    // 데이터 업데이트 함수
    function updateDataUI() {
        const filter = $('#search-filter').val().toLowerCase();
        let filteredData = data;
        if (filter) {
            filteredData = data.filter(item =>
                (settings.dataFields.name && item.name.toLowerCase().includes(filter)) ||
                (settings.dataFields.url && item.url.toLowerCase().includes(filter)) ||
                (settings.dataFields.description && item.description.toLowerCase().includes(filter)) ||
                (settings.dataFields.html && item.html.toLowerCase().includes(filter)) ||
                (settings.dataFields.category && item.category.toLowerCase().includes(filter)) ||
                (settings.dataFields.language && item.language && item.language.toLowerCase().includes(filter)) ||
                (settings.dataFields.wordCount && item.wordCount.toString().includes(filter)) ||
                (settings.dataFields.metaTags && item.metaTags && Object.values(item.metaTags).some(v => v.toLowerCase().includes(filter))) ||
                (settings.dataFields.headings && item.headings && item.headings.some(h => h.toLowerCase().includes(filter)))
            );
        }

        let tableHtml = filteredData.map(item => `
            <tr>
                ${settings.dataFields.name ? `<td>${escapeHTML(item.name)}</td>` : ''}
                ${settings.dataFields.url ? `<td><a href="${escapeHTML(item.url)}" target="_blank">${escapeHTML(item.url)}</a></td>` : ''}
                ${settings.dataFields.description ? `<td>${escapeHTML(item.description)}</td>` : ''}
                ${settings.dataFields.html ? `<td><button class="view-html" data-html="${encodeURIComponent(item.html)}" title="HTML 내용을 봅니다.">📄 보기</button></td>` : ''}
                ${settings.dataFields.category ? `<td>${escapeHTML(item.category)}</td>` : ''}
                ${settings.dataFields.quality ? `<td>${escapeHTML(item.quality.toString())}</td>` : ''}
                ${settings.dataFields.language ? `<td>${escapeHTML(item.language)}</td>` : ''}
                ${settings.dataFields.wordCount ? `<td>${escapeHTML(item.wordCount.toString())}</td>` : ''}
                ${settings.dataFields.metaTags ? `<td>${escapeHTML(JSON.stringify(item.metaTags))}</td>` : ''}
                ${settings.dataFields.headings ? `<td>${escapeHTML(item.headings.join(', '))}</td>` : ''}
            </tr>
        `).join('');
        $('#data-table-body').html(tableHtml);

        // HTML 보기 모달
        $('.view-html').off('click').on('click', function() {
            const htmlContent = decodeURIComponent($(this).data('html'));
            $('#modal-html-content').text(htmlContent);
            $('#html-modal').show();
        });

        if (settings.dataVisualization) {
            renderChart();
        }
    }

    // 저장된 사이트 UI 업데이트 함수
    function updateSavedSitesUI() {
        const savedSitesHtml = settings.savedSites.map(site => `
            <tr>
                <td><a href="${escapeHTML(site)}" target="_blank">${escapeHTML(site)}</a></td>
                <td><button class="remove-saved-site" data-site="${escapeHTML(site)}">삭제</button></td>
            </tr>
        `).join('');
        $('#saved-sites-table-body').html(savedSitesHtml);
    }

    // 차트 렌더링 함수
    function renderChart() {
        if (!document.getElementById('data-chart').getContext) return;

        const ctx = document.getElementById('data-chart').getContext('2d');

        // 기존 차트 제거
        if (window.dataChartInstance) {
            window.dataChartInstance.destroy();
        }

        const categories = {};
        data.forEach(item => {
            const category = item.category || '기타';
            if (categories[category]) {
                categories[category]++;
            } else {
                categories[category] = 1;
            }
        });

        const chartData = {
            labels: Object.keys(categories),
            datasets: [{
                label: '카테고리별 데이터 수',
                data: Object.values(categories),
                backgroundColor: generateColors(Object.keys(categories).length)
            }]
        };

        window.dataChartInstance = new Chart(ctx, {
            type: 'pie',
            data: chartData,
            options: {
                responsive: true
            }
        });
    }

    // 색상 생성 함수
    function generateColors(count) {
        const colors = [];
        for(let i=0; i<count; i++) {
            const hue = i * (360 / count);
            colors.push(`hsl(${hue}, 70%, 50%)`);
        }
        return colors;
    }

    // HTML 이스케이프 함수
    function escapeHTML(str) {
        if (!str) return '';
        return str.replace(/&/g, "&amp;")
                  .replace(/</g, "&lt;")
                  .replace(/>/g, "&gt;")
                  .replace(/"/g, "&quot;")
                  .replace(/'/g, "&#039;");
    }

    // CSV, JSON, XML, XLS 다운로드 함수
    function downloadCSV() {
        if (data.length === 0) {
            log('다운로드할 데이터가 없습니다.', 'warning');
            return;
        }
        const headers = [];
        if (settings.dataFields.name) headers.push("사이트 이름");
        if (settings.dataFields.url) headers.push("주소");
        if (settings.dataFields.description) headers.push("설명");
        if (settings.dataFields.html) headers.push("📄 HTML 내용");
        if (settings.dataFields.category) headers.push("분류");
        if (settings.dataFields.quality) headers.push("품질점수");
        if (settings.dataFields.language) headers.push("언어");
        if (settings.dataFields.wordCount) headers.push("단어 수");
        if (settings.dataFields.metaTags) headers.push("메타 태그");
        if (settings.dataFields.headings) headers.push("헤딩");

        const csvRows = [
            headers.join(",")
        ];

        data.forEach(d => {
            const row = [];
            if (settings.dataFields.name) row.push(`"${escapeCSV(d.name)}"`);
            if (settings.dataFields.url) row.push(`"${escapeCSV(d.url)}"`);
            if (settings.dataFields.description) row.push(`"${escapeCSV(d.description)}"`);
            if (settings.dataFields.html) row.push(`"${escapeCSV(d.html)}"`);
            if (settings.dataFields.category) row.push(`"${escapeCSV(d.category)}"`);
            if (settings.dataFields.quality) row.push(`"${d.quality}"`);
            if (settings.dataFields.language) row.push(`"${escapeCSV(d.language)}"`);
            if (settings.dataFields.wordCount) row.push(`"${d.wordCount}"`);
            if (settings.dataFields.metaTags) row.push(`"${escapeCSV(JSON.stringify(d.metaTags))}"`);
            if (settings.dataFields.headings) row.push(`"${escapeCSV(d.headings.join('; '))}"`);
            csvRows.push(row.join(","));
        });

        const csvContent = "data:text/csv;charset=utf-8," + csvRows.join("\n");
        const encodedUri = encodeURI(csvContent);
        const link = document.createElement("a");
        link.setAttribute("href", encodedUri);
        const filename = "data.csv";
        link.setAttribute("download", filename);
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        log('CSV 파일이 다운로드되었습니다.', 'success');
    }

    function downloadJSON() {
        if (data.length === 0) {
            log('다운로드할 데이터가 없습니다.', 'warning');
            return;
        }
        const jsonContent = JSON.stringify(data, null, 2);
        const blob = new Blob([jsonContent], { type: "application/json" });
        const url = URL.createObjectURL(blob);
        const link = document.createElement("a");
        link.setAttribute("href", url);
        const filename = "data.json";
        link.setAttribute("download", filename);
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        log('JSON 파일이 다운로드되었습니다.', 'success');
    }

    function downloadXML() {
        if (data.length === 0) {
            log('다운로드할 데이터가 없습니다.', 'warning');
            return;
        }
        let xmlContent = '<?xml version="1.0" encoding="UTF-8"?>\n<data>\n';
        data.forEach(d => {
            xmlContent += `  <entry>\n`;
            if (settings.dataFields.name) xmlContent += `    <name>${escapeXML(d.name)}</name>\n`;
            if (settings.dataFields.url) xmlContent += `    <url>${escapeXML(d.url)}</url>\n`;
            if (settings.dataFields.description) xmlContent += `    <description>${escapeXML(d.description)}</description>\n`;
            if (settings.dataFields.html) xmlContent += `    <html><![CDATA[${d.html}]]></html>\n`;
            if (settings.dataFields.category) xmlContent += `    <category>${escapeXML(d.category)}</category>\n`;
            if (settings.dataFields.quality) xmlContent += `    <quality>${d.quality}</quality>\n`;
            if (settings.dataFields.language) xmlContent += `    <language>${escapeXML(d.language)}</language>\n`;
            if (settings.dataFields.wordCount) xmlContent += `    <wordCount>${d.wordCount}</wordCount>\n`;
            if (settings.dataFields.metaTags) xmlContent += `    <metaTags>${escapeXML(JSON.stringify(d.metaTags))}</metaTags>\n`;
            if (settings.dataFields.headings) xmlContent += `    <headings>${escapeXML(d.headings.join('; '))}</headings>\n`;
            xmlContent += `  </entry>\n`;
        });
        xmlContent += '</data>';

        const blob = new Blob([xmlContent], { type: "application/xml" });
        const url = URL.createObjectURL(blob);
        const link = document.createElement("a");
        link.setAttribute("href", url);
        const filename = "data.xml";
        link.setAttribute("download", filename);
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        log('XML 파일이 다운로드되었습니다.', 'success');
    }

    function downloadXLS() {
        if (data.length === 0) {
            log('다운로드할 데이터가 없습니다.', 'warning');
            return;
        }
        const worksheet = XLSX.utils.json_to_sheet(data.map(d => {
            const entry = {};
            if (settings.dataFields.name) entry['사이트 이름'] = d.name;
            if (settings.dataFields.url) entry['주소'] = d.url;
            if (settings.dataFields.description) entry['설명'] = d.description;
            if (settings.dataFields.html) entry['📄 HTML 내용'] = d.html;
            if (settings.dataFields.category) entry['분류'] = d.category;
            if (settings.dataFields.quality) entry['품질점수'] = d.quality;
            if (settings.dataFields.language) entry['언어'] = d.language;
            if (settings.dataFields.wordCount) entry['단어 수'] = d.wordCount;
            if (settings.dataFields.metaTags) entry['메타 태그'] = JSON.stringify(d.metaTags);
            if (settings.dataFields.headings) entry['헤딩'] = d.headings.join('; ');
            return entry;
        }));
        const workbook = XLSX.utils.book_new();
        XLSX.utils.book_append_sheet(workbook, worksheet, "Data");
        XLSX.writeFile(workbook, "data.xlsx");
        log('XLS 파일이 다운로드되었습니다.', 'success');
    }

    // XML 이스케이프 함수
    function escapeXML(str) {
        if (!str) return '';
        return str.replace(/&/g, "&amp;")
                  .replace(/</g, "&lt;")
                  .replace(/>/g, "&gt;")
                  .replace(/"/g, "&quot;")
                  .replace(/'/g, "&#039;");
    }

    // CSV 이스케이프 함수
    function escapeCSV(str) {
        if (!str) return '';
        return str.replace(/"/g, '""');
    }

    // 테이블 필터링 함수
    function filterTable(keyword) {
        $('#data-table-body tr').each(function() {
            const rowText = $(this).text().toLowerCase();
            if (rowText.includes(keyword.toLowerCase())) {
                $(this).show();
            } else {
                $(this).hide();
            }
        });
    }

    // 설정 저장 함수
    function saveSettings() {
        const delay = parseInt($('#delay-input').val(), 10);
        settings.delay = isNaN(delay) ? defaultSettings.delay : delay;

        const delayRandom = parseInt($('#delay-random-input').val(), 10);
        settings.delayRandom = isNaN(delayRandom) ? defaultSettings.delayRandom : delayRandom;

        const maxPages = parseInt($('#max-pages-input').val(), 10);
        settings.maxPages = isNaN(maxPages) ? defaultSettings.maxPages : maxPages;

        // 사용자 에이전트 선택
        const selectedUAIndex = parseInt($('#user-agent-select').val(), 10);
        settings.userAgentIndex = isNaN(selectedUAIndex) ? 0 : selectedUAIndex;

        const contentInclude = $('#content-include-input').val().split(',').map(ct => ct.trim()).filter(ct => ct);
        const contentExclude = $('#content-exclude-input').val().split(',').map(ct => ct.trim()).filter(ct => ct);
        settings.contentTypes.include = contentInclude.length > 0 ? contentInclude : defaultSettings.contentTypes.include;
        settings.contentTypes.exclude = contentExclude.length > 0 ? contentExclude : defaultSettings.contentTypes.exclude;

        const timeout = parseInt($('#timeout-input').val(), 10);
        settings.timeout = isNaN(timeout) ? defaultSettings.timeout : timeout;

        const includeKeywords = $('#include-keywords-input').val().split('\n').map(kw => kw.trim()).filter(kw => kw);
        settings.includeKeywords = includeKeywords;

        const excludeKeywords = $('#exclude-keywords-input').val().split('\n').map(kw => kw.trim()).filter(kw => kw);
        settings.excludeKeywords = excludeKeywords;

        const regexInclude = $('#regex-include-input').val().split('\n').map(rx => rx.trim()).filter(rx => rx);
        settings.regexInclude = regexInclude;

        const regexExclude = $('#regex-exclude-input').val().split('\n').map(rx => rx.trim()).filter(rx => rx);
        settings.regexExclude = regexExclude;

        const language = $('#language-select').val() || [];
        settings.language = language;

        const minWordCount = parseInt($('#min-word-count-input').val(), 10);
        settings.minWordCount = isNaN(minWordCount) ? defaultSettings.minWordCount : minWordCount;

        const maxWordCount = parseInt($('#max-word-count-input').val(), 10);
        settings.maxWordCount = isNaN(maxWordCount) ? defaultSettings.maxWordCount : maxWordCount;

        const maxDepth = parseInt($('#max-depth-input').val(), 10);
        settings.maxDepth = isNaN(maxDepth) ? defaultSettings.maxDepth : maxDepth;

        const proxy = $('#proxy-input').val().trim();
        settings.proxy = proxy || defaultSettings.proxy;

        // 데이터 필드 설정
        settings.dataFields.name = $('#field-name').is(':checked');
        settings.dataFields.url = $('#field-url').is(':checked');
        settings.dataFields.description = $('#field-description').is(':checked');
        settings.dataFields.html = $('#field-html').is(':checked');
        settings.dataFields.category = $('#field-category').is(':checked');
        settings.dataFields.quality = $('#field-quality').is(':checked');
        settings.dataFields.language = $('#field-language').is(':checked');
        settings.dataFields.wordCount = $('#field-wordCount').is(':checked');
        settings.dataFields.metaTags = $('#field-metaTags').is(':checked');
        settings.dataFields.headings = $('#field-headings').is(':checked');

        // 재시도 설정
        const retryAttempts = parseInt($('#retry-attempts-input').val(), 10);
        settings.retryAttempts = isNaN(retryAttempts) ? defaultSettings.retryAttempts : retryAttempts;

        const retryDelay = parseInt($('#retry-delay-input').val(), 10);
        settings.retryDelay = isNaN(retryDelay) ? defaultSettings.retryDelay : retryDelay;

        // 스케줄링 설정
        settings.schedule.enabled = $('#schedule-enabled').is(':checked');
        const startTime = $('#start-time-input').val();
        settings.schedule.startTime = startTime || defaultSettings.schedule.startTime;
        const interval = parseInt($('#interval-input').val(), 10);
        settings.schedule.interval = isNaN(interval) ? defaultSettings.schedule.interval : interval * 60000; // 분을 밀리초로 변환

        // 검색 엔진 설정
        const searchEngines = {
            google: $('#engine-google').is(':checked'),
            bing: $('#engine-bing').is(':checked'),
            yahoo: $('#engine-yahoo').is(':checked'),
            duckduckgo: $('#engine-duckduckgo').is(':checked'),
            baidu: $('#engine-baidaodu').is(':checked'), // 수정: 'baidu' 오타 수정
            yandex: $('#engine-yandex').is(':checked'),
            naver: $('#engine-naver').is(':checked'),
            daum: $('#engine-daum').is(':checked'),
            ask: $('#engine-ask').is(':checked'),
            ecosia: $('#engine-ecosia').is(':checked'),
            startpage: $('#engine-startpage').is(':checked'),
            searx: $('#engine-searx').is(':checked'),
            dogpile: $('#engine-dogpile').is(':checked'),
            bingimages: $('#engine-bingimages').is(':checked')
            // 추가 검색 엔진 옵션
        };
        settings.searchEngines = searchEngines;

        // robots.txt 준수 설정
        settings.respectRobots = $('#respect-robots-checkbox').is(':checked');

        const blacklist = $('#blacklist-input').val().split('\n').map(d => d.trim()).filter(d => d);
        settings.blacklist = blacklist;

        const whitelist = $('#whitelist-input').val().split('\n').map(d => d.trim()).filter(d => d);
        settings.whitelist = whitelist;

        const maxConcurrentRequests = parseInt($('#max-concurrent-requests-input').val(), 10);
        settings.maxConcurrentRequests = isNaN(maxConcurrentRequests) ? defaultSettings.maxConcurrentRequests : maxConcurrentRequests;

        // 데이터 시각화 설정
        settings.dataVisualization = $('#data-visualization-checkbox').is(':checked');
        GM_setValue('dataVisualization', settings.dataVisualization);

        // CAPTCHA 처리 설정
        settings.enableCaptchas = $('#enable-captchas-checkbox').is(':checked');
        GM_setValue('enableCaptchas', settings.enableCaptchas);

        // 내보내기 형식 설정
        settings.exportFormats.csv = $('#export-csv').is(':checked');
        settings.exportFormats.json = $('#export-json').is(':checked');
        settings.exportFormats.xml = $('#export-xml').is(':checked');
        settings.exportFormats.xls = $('#export-xls').is(':checked');

        // 고급 로깅 설정
        settings.advancedLogging = $('#advanced-logging-checkbox').is(':checked');
        GM_setValue('advancedLogging', settings.advancedLogging);

        // 사용자 지정 키워드 분류 저장
        const customKeywordsText = $('#custom-keywords-input').val();
        const customKeywords = parseCustomKeywords(customKeywordsText);
        settings.customKeywords = customKeywords;
        GM_setValue('customKeywords', customKeywords);

        // 저장
        GM_setValue('delay', settings.delay);
        GM_setValue('delayRandom', settings.delayRandom);
        GM_setValue('maxPages', settings.maxPages);
        GM_setValue('userAgentIndex', settings.userAgentIndex);
        GM_setValue('contentTypes', settings.contentTypes);
        GM_setValue('timeout', settings.timeout);
        GM_setValue('includeKeywords', settings.includeKeywords);
        GM_setValue('excludeKeywords', settings.excludeKeywords);
        GM_setValue('regexInclude', settings.regexInclude);
        GM_setValue('regexExclude', settings.regexExclude);
        GM_setValue('language', settings.language);
        GM_setValue('minWordCount', settings.minWordCount);
        GM_setValue('maxWordCount', settings.maxWordCount);
        GM_setValue('maxDepth', settings.maxDepth);
        GM_setValue('proxy', settings.proxy);
        GM_setValue('dataFields', settings.dataFields);
        GM_setValue('retryAttempts', settings.retryAttempts);
        GM_setValue('retryDelay', settings.retryDelay);
        GM_setValue('schedule', settings.schedule);
        GM_setValue('searchEngines', settings.searchEngines);
        GM_setValue('blacklist', settings.blacklist);
        GM_setValue('whitelist', settings.whitelist);
        GM_setValue('respectRobots', settings.respectRobots);
        GM_setValue('maxConcurrentRequests', settings.maxConcurrentRequests);
        GM_setValue('exportFormats', settings.exportFormats);

        // totalPages 갱신
        totalPages = settings.maxPages;
        GM_setValue('totalPages', totalPages);

        log('설정이 저장되었습니다.', 'success');

        // 스케줄링 설정이 활성화된 경우 스케줄링 시작
        if (settings.schedule.enabled) {
            scheduleCrawling();
        }

        // UI 업데이트
        updateDataUI();
        updateSavedSitesUI();
        if (settings.dataVisualization) {
            renderChart();
        }
    }

    // 설정 초기화 함수
    function resetSettings() {
        if (confirm("기본 설정으로 초기화하시겠습니까?")) {
            // 기본 설정으로 되돌리기
            Object.keys(defaultSettings).forEach(key => {
                GM_setValue(key, defaultSettings[key]);
            });

            // 기존 변수 업데이트
            Object.assign(settings, defaultSettings);
            settings.userAgents = defaultSettings.userAgents;
            settings.userAgentIndex = defaultSettings.userAgentIndex;
            settings.contentTypes = defaultSettings.contentTypes;
            settings.dataFields = defaultSettings.dataFields;
            settings.exportFormats = defaultSettings.exportFormats;
            settings.advancedLogging = defaultSettings.advancedLogging;
            settings.enableCaptchas = defaultSettings.enableCaptchas;
            settings.savedSites = defaultSettings.savedSites;

            // UI 업데이트
            $('input, textarea, select').each(function() {
                const id = $(this).attr('id');
                if (settings.hasOwnProperty(id)) {
                    $(this).val(settings[id]);
                }
            });

            // 사용자 지정 키워드 초기화
            $('#custom-keywords-input').val('');

            updateUserAgentSelect();
            updateDataUI();
            updateSavedSitesUI();
            if (settings.dataVisualization) {
                renderChart();
            }
            log('기본 설정으로 초기화되었습니다.', 'success');
        }
    }

    // 크롤링 시작 함수
    async function startCrawling() {
        if (isCrawling) {
            log('이미 크롤링이 진행 중입니다.', 'warning');
            return;
        }
        // 활성화된 검색 엔진 확인
        if (!isAnySearchEngineEnabled()) {
            log('사용할 검색 엔진이 하나도 선택되지 않았습니다.', 'error');
            return;
        }
        isCrawling = true;
        GM_setValue('isCrawling', isCrawling);
        log('크롤링을 시작합니다.', 'info');
        setButtonStates();
        currentPage = GM_getValue('currentPage', 1);
        totalPages = GM_getValue('totalPages', settings.maxPages);
        GM_setValue('totalPages', totalPages);
        currentDepth = GM_getValue('currentDepth', 0);
        await crawlPage(currentDepth);
    }

    // 크롤링 중지 함수
    function stopCrawling() {
        if (!isCrawling) {
            log('크롤링이 진행 중이지 않습니다.', 'warning');
            return;
        }
        isCrawling = false;
        GM_setValue('isCrawling', isCrawling);
        log('크롤링이 중지되었습니다.', 'warning');
        setButtonStates();
    }

    // 버튼 상태 설정 함수
    function setButtonStates() {
        if (isCrawling) {
            $('#start-crawling').prop('disabled', true);
            $('#stop-crawling').prop('disabled', false);
        } else {
            $('#start-crawling').prop('disabled', false);
            $('#stop-crawling').prop('disabled', true);
        }
    }

    // 크롤링 시작: 기존 페이지에서 크롤링 재개
    async function resumeCrawling() {
        if (isCrawling) {
            await crawlPage(currentDepth);
        }
    }

    // 크롤링 함수
    async function crawlPage(currentDepthLevel) {
        try {
            while (isCrawling && currentPage <= totalPages && currentDepthLevel <= settings.maxDepth) {
                log(`페이지 ${currentPage} 크롤링 시작. (깊이: ${currentDepthLevel})`, 'info');

                // robots.txt 준수 체크
                if (settings.respectRobots) {
                    const robotsAllowed = await checkRobotsTxt(window.location.href);
                    if (!robotsAllowed) {
                        log(`robots.txt에 의해 크롤링이 금지된 페이지: ${window.location.href}`, 'warning');
                        break;
                    }
                }

                const searchResults = extractSearchResults();
                if (!searchResults) {
                    log('검색 결과를 찾을 수 없습니다.', 'error');
                    break;
                }

                for (let result of searchResults) {
                    if (!isCrawling) break;

                    const name = result.name;
                    const link = result.url;
                    const description = result.description;

                    // 중복 검사
                    if (visitedUrls.includes(link)) {
                        log(`중복된 URL 발견: ${link}`, 'warning');
                        continue;
                    }

                    // 블랙리스트/화이트리스트 검사
                    if (settings.whitelist.length > 0 && !settings.whitelist.some(domain => link.includes(domain))) {
                        log(`화이트리스트에 없는 도메인: ${link}`, 'warning');
                        continue;
                    }
                    if (settings.blacklist.some(domain => link.includes(domain))) {
                        log(`블랙리스트에 있는 도메인: ${link}`, 'warning');
                        continue;
                    }

                    // 저장된 사이트 검사
                    if (settings.savedSites.length > 0 && !settings.savedSites.includes(link)) {
                        log(`저장된 사이트 목록에 없는 URL: ${link}`, 'warning');
                        continue;
                    }

                    // 언어 필터링 검사
                    if (settings.language.length > 0) {
                        const pageLanguage = await getPageLanguage(link);
                        if (!settings.language.includes(pageLanguage)) {
                            log(`선택된 언어에 맞지 않는 페이지: ${link} (${pageLanguage})`, 'warning');
                            continue;
                        }
                    }

                    // 키워드 포함/제외 검사
                    if (settings.includeKeywords.length > 0 && !settings.includeKeywords.some(kw => link.includes(kw) || description.includes(kw))) {
                        log(`포함 키워드가 없는 URL: ${link}`, 'warning');
                        continue;
                    }
                    if (settings.excludeKeywords.some(kw => link.includes(kw) || description.includes(kw))) {
                        log(`제외 키워드가 포함된 URL: ${link}`, 'warning');
                        continue;
                    }

                    // 정규 표현식 필터링
                    if (settings.regexInclude.length > 0) {
                        const matchesInclude = settings.regexInclude.some(pattern => new RegExp(pattern).test(link) || new RegExp(pattern).test(description));
                        if (!matchesInclude) {
                            log(`정규 표현식 포함 조건에 맞지 않는 URL: ${link}`, 'warning');
                            continue;
                        }
                    }
                    if (settings.regexExclude.length > 0) {
                        const matchesExclude = settings.regexExclude.some(pattern => new RegExp(pattern).test(link) || new RegExp(pattern).test(description));
                        if (matchesExclude) {
                            log(`정규 표현식 제외 조건에 맞는 URL: ${link}`, 'warning');
                            continue;
                        }
                    }

                    // 큐에 추가
                    requestQueue.push({ url: link, depth: currentDepthLevel });

                    // 최대 동시 요청 도달 시 대기
                    if (activeRequests >= settings.maxConcurrentRequests) {
                        break;
                    }
                }

                // 큐 처리 시작
                processQueue();

                // 진행률 업데이트
                progress = (currentPage / totalPages) * 100;
                GM_setValue('progress', progress);
                updateLogsUI();

                // 다음 페이지로 이동
                if (currentPage < totalPages && isCrawling) {
                    const nextPageUrl = getNextPageUrl();
                    if (nextPageUrl) {
                        currentPage++;
                        GM_setValue('currentPage', currentPage);
                        currentDepth++;
                        GM_setValue('currentDepth', currentDepth);
                        log(`다음 페이지로 이동: ${currentPage}`, 'info');
                        await dynamicDelay();
                        window.location.href = nextPageUrl;
                        return; // 페이지 이동 후 크롤링 이어서 하기 위해 함수 종료
                    } else {
                        log('다음 페이지 링크를 찾을 수 없습니다.', 'error');
                        break;
                    }
                } else {
                    break;
                }
            }

            // 큐가 비어있고 모든 요청이 완료된 경우 크롤링 완료
            if (isCrawling && currentPage > totalPages && activeRequests === 0 && requestQueue.length === 0) {
                log('크롤링이 완료되었습니다.', 'success');
                isCrawling = false;
                GM_setValue('isCrawling', isCrawling);
                setButtonStates();
                GM_setValue('progress', 100);
                $('.progress-bar-fill').css('width', `100%`);
                $('.status-text').text(`진행률: 100% (${currentPage}/${totalPages} 페이지)`);
            }
        } catch (error) {
            log(`크롤링 중 오류 발생: ${error.message}`, 'bug'); // 버그 로그로 기록
        } finally {
            // 크롤링이 아직 진행 중인 경우 state를 유지
            if (!isCrawling) {
                // 크롤링이 중지된 경우에만 상태를 초기화
                GM_setValue('isCrawling', isCrawling);
                setButtonStates();
                log('크롤링이 중지되었습니다.', 'info');
            }
            // 페이지 이동 시에는 상태를 유지하고, 다음 페이지에서 계속 크롤링
        }
    }

    // 동시 요청 제어 함수
    async function processQueue() {
        while (requestQueue.length > 0 && activeRequests < settings.maxConcurrentRequests) {
            const { url, depth } = requestQueue.shift();
            activeRequests++;
            try {
                await handleRequest(url, depth);
            } catch (error) {
                log(`요청 처리 중 오류 발생: ${url} - ${error.message}`, 'bug');
            } finally {
                activeRequests--;
                processQueue();
            }
        }
    }

    async function handleRequest(url, depthLevel) {
        if (!isCrawling) return;

        // 중복 검사
        if (visitedUrls.includes(url)) {
            log(`중복된 URL 발견: ${url}`, 'warning');
            return;
        }

        // 블랙리스트/화이트리스트 검사
        if (settings.whitelist.length > 0 && !settings.whitelist.some(domain => url.includes(domain))) {
            log(`화이트리스트에 없는 도메인: ${url}`, 'warning');
            return;
        }
        if (settings.blacklist.some(domain => url.includes(domain))) {
            log(`블랙리스트에 있는 도메인: ${url}`, 'warning');
            return;
        }

        // 저장된 사이트 검사
        if (settings.savedSites.length > 0 && !settings.savedSites.includes(url)) {
            log(`저장된 사이트 목록에 없는 URL: ${url}`, 'warning');
            return;
        }

        // 언어 필터링 검사
        if (settings.language.length > 0) {
            const pageLanguage = await getPageLanguage(url);
            if (!settings.language.includes(pageLanguage)) {
                log(`선택된 언어에 맞지 않는 페이지: ${url} (${pageLanguage})`, 'warning');
                return;
            }
        }

        // 키워드 포함/제외 검사
        if (settings.includeKeywords.length > 0 && !settings.includeKeywords.some(kw => url.includes(kw))) {
            log(`포함 키워드가 없는 URL: ${url}`, 'warning');
            return;
        }
        if (settings.excludeKeywords.some(kw => url.includes(kw))) {
            log(`제외 키워드가 포함된 URL: ${url}`, 'warning');
            return;
        }

        // 정규 표현식 필터링
        if (settings.regexInclude.length > 0) {
            const matchesInclude = settings.regexInclude.some(pattern => new RegExp(pattern).test(url));
            if (!matchesInclude) {
                log(`정규 표현식 포함 조건에 맞지 않는 URL: ${url}`, 'warning');
                return;
            }
        }
        if (settings.regexExclude.length > 0) {
            const matchesExclude = settings.regexExclude.some(pattern => new RegExp(pattern).test(url));
            if (matchesExclude) {
                log(`정규 표현식 제외 조건에 맞는 URL: ${url}`, 'warning');
                return;
            }
        }

        // 데이터 수집
        try {
            const response = await fetchWithRetry(url, settings.retryAttempts, settings.retryDelay);
            const contentType = getContentType(response);
            if (!isContentTypeAllowed(contentType)) {
                log(`허용되지 않은 콘텐츠 유형(${contentType})의 URL: ${url}`, 'warning');
                return;
            }

            const pageHtml = response.responseText;
            const parser = new DOMParser();
            const pageDoc = parser.parseFromString(pageHtml, 'text/html');
            const textContent = pageDoc.body ? pageDoc.body.innerText : '';

            // CAPTCHA 감지 및 처리
            if (settings.enableCaptchas && detectCaptcha(pageDoc)) {
                await handleCaptcha(url);
                return;
            }

            // 단어 수 검사
            const wordCount = textContent.split(/\s+/).length;
            if (wordCount < settings.minWordCount) {
                log(`단어 수가 최소 기준 (${settings.minWordCount})에 미치지 못하는 URL: ${url} (${wordCount} 단어)`, 'warning');
                return;
            }
            if (wordCount > settings.maxWordCount) {
                log(`단어 수가 최대 기준 (${settings.maxWordCount})을 초과하는 URL: ${url} (${wordCount} 단어)`, 'warning');
                return;
            }

            // 콘텐츠 품질 평가
            const qualityScore = evaluateContentQuality(textContent);

            // 언어 추출
            const language = pageDoc.documentElement.lang || 'unknown';

            // 키워드 분류
            const category = classifyKeywords(textContent);

            // 메타 태그 및 헤딩 추출
            const metaTags = extractMetaTags(pageDoc);
            const headings = extractHeadings(pageDoc);

            // 조건에 맞는 경우에만 데이터 추가
            if (category !== '기타' || qualityScore > 100) { // 예시 조건
                const dataEntry = {};
                if (settings.dataFields.name) dataEntry.name = extractName(pageDoc) || 'No Title';
                if (settings.dataFields.url) dataEntry.url = url;
                if (settings.dataFields.description) dataEntry.description = extractDescription(pageDoc) || 'No Description';
                if (settings.dataFields.html) dataEntry.html = pageHtml;
                if (settings.dataFields.category) dataEntry.category = category;
                if (settings.dataFields.quality) dataEntry.quality = qualityScore;
                if (settings.dataFields.language) dataEntry.language = language;
                if (settings.dataFields.wordCount) dataEntry.wordCount = wordCount;
                if (settings.dataFields.metaTags) dataEntry.metaTags = metaTags;
                if (settings.dataFields.headings) dataEntry.headings = headings;

                data.push(dataEntry);
                GM_setValue('data', data);
                updateDataUI();
                log(`데이터 수집 완료: ${url}`, 'success');

                // 데이터 시각화 업데이트
                if (settings.dataVisualization) {
                    renderChart();
                }
            } else {
                log(`조건에 맞지 않아 데이터에 추가되지 않은 URL: ${url}`, 'info');
            }

            // 링크 추적을 위한 추가 로직 (심층 크롤링)
            if (depthLevel < settings.maxDepth) {
                const additionalLinks = extractLinks(pageDoc);
                for (let additionalLink of additionalLinks) {
                    if (!visitedUrls.includes(additionalLink) && isValidURL(additionalLink)) {
                        requestQueue.push({ url: additionalLink, depth: depthLevel + 1 });
                    }
                }
            }

        } catch (error) {
            log(`데이터 수집 실패: ${url} - ${error.message}`, 'bug');
            // 실패한 경우 data에 추가하지 않음
        }

        visitedUrls.push(url);
        GM_setValue('visitedUrls', visitedUrls);
    }

    // 크롤링 함수에 재시도 추가
    async function fetchWithRetry(url, attempts, delayMs) {
        for (let attempt = 1; attempt <= attempts; attempt++) {
            try {
                const response = await GM_xmlhttpRequestPromise(url);
                return response;
            } catch (error) {
                if (attempt < attempts) {
                    log(`요청 실패 (${attempt}/${attempts}): ${url} - ${error.message}`, 'warning');
                    await delay(delayMs);
                } else {
                    throw error;
                }
            }
        }
    }

    // 활성화된 검색 엔진이 있는지 확인
    function isAnySearchEngineEnabled() {
        return Object.values(settings.searchEngines).some(enabled => enabled);
    }

    // 사용자 지정 키워드 분류 통합
    function classifyKeywords(text) {
        const standardKeywords = combineKeywords();
        for (const [category, kws] of Object.entries(standardKeywords)) {
            if (kws.some(kw => text.includes(kw))) {
                return category;
            }
        }
        return '기타';
    }

    // 검색 결과 추출 함수 (각 검색 엔진에 맞게 수정됨)
    function extractSearchResults() {
        let results = [];
        // 구글 검색 결과 추출 (업데이트된 셀렉터)
        if (window.location.hostname.includes('google.com') && settings.searchEngines.google) {
            const items = document.querySelectorAll('div.g, div[data-hveid]');
            items.forEach(item => {
                const nameElement = item.querySelector('h3');
                const linkElement = item.querySelector('a');
                let descElement = item.querySelector('.aCOpRe');
                if (!descElement) {
                    descElement = item.querySelector('.VwiC3b');
                }

                const name = nameElement ? nameElement.innerText.trim() : 'No Title';
                const link = linkElement ? linkElement.href : '';
                const description = descElement ? descElement.innerText.trim() : 'No Description';

                if (link && isValidURL(link)) results.push({ name, url: link, description });
            });
        }
        // 빙 검색 결과 추출 (업데이트된 셀렉터)
        else if (window.location.hostname.includes('bing.com') && settings.searchEngines.bing) {
            const items = document.querySelectorAll('.b_algo');
            items.forEach(item => {
                const nameElement = item.querySelector('h2');
                const linkElement = nameElement ? nameElement.querySelector('a') : null;
                const descElement = item.querySelector('.b_caption p');

                const name = nameElement ? nameElement.innerText.trim() : 'No Title';
                const link = linkElement ? linkElement.href : '';
                const description = descElement ? descElement.innerText.trim() : 'No Description';

                if (link && isValidURL(link)) results.push({ name, url: link, description });
            });
        }
        // 야후 검색 결과 추출 (업데이트된 셀렉터)
        else if (window.location.hostname.includes('search.yahoo.com') && settings.searchEngines.yahoo) {
            const items = document.querySelectorAll('div.dd.algo');
            items.forEach(item => {
                const nameElement = item.querySelector('h3.title');
                const linkElement = nameElement ? nameElement.querySelector('a') : null;
                const descElement = item.querySelector('.compText p');

                const name = nameElement ? nameElement.innerText.trim() : 'No Title';
                const link = linkElement ? linkElement.href : '';
                const description = descElement ? descElement.innerText.trim() : 'No Description';

                if (link && isValidURL(link)) results.push({ name, url: link, description });
            });
        }
        // 네이버 검색 결과 추출 (업데이트된 셀렉터)
        else if (window.location.hostname.includes('naver.com') && settings.searchEngines.naver) {
            const items = document.querySelectorAll('.news.mynews.section._prs_nws');
            items.forEach(item => {
                const nameElement = item.querySelector('.news_area .news_tit');
                const linkElement = nameElement ? nameElement.href : '';
                const descElement = item.querySelector('.dsc_wrap');

                const name = nameElement ? nameElement.innerText.trim() : 'No Title';
                const link = linkElement ? linkElement : '';
                const description = descElement ? descElement.innerText.trim() : 'No Description';

                if (link && isValidURL(link)) results.push({ name, url: link, description });
            });
        }
        // 다음 검색 결과 추출 (업데이트된 셀렉터)
        else if (window.location.hostname.includes('daum.net') && settings.searchEngines.daum) {
            const items = document.querySelectorAll('.wrap_cont div');
            items.forEach(item => {
                const nameElement = item.querySelector('a.f_link_b');
                const linkElement = nameElement ? nameElement.href : '';
                const descElement = item.querySelector('.desc');

                const name = nameElement ? nameElement.innerText.trim() : 'No Title';
                const link = linkElement ? linkElement : '';
                const description = descElement ? descElement.innerText.trim() : 'No Description';

                if (link && isValidURL(link)) results.push({ name, url: link, description });
            });
        }
        // DuckDuckGo 검색 결과 추출 (업데이트된 셀렉터)
        else if (window.location.hostname.includes('duckduckgo.com') && settings.searchEngines.duckduckgo) {
            const items = document.querySelectorAll('.result');
            items.forEach(item => {
                const nameElement = item.querySelector('h2 a.result__a');
                const linkElement = nameElement ? nameElement.href : '';
                const descElement = item.querySelector('.result__snippet');

                const name = nameElement ? nameElement.innerText.trim() : 'No Title';
                const link = linkElement ? linkElement : '';
                const description = descElement ? descElement.innerText.trim() : 'No Description';

                if (link && isValidURL(link)) results.push({ name, url: link, description });
            });
        }
        // Baidu 검색 결과 추출 (예시)
        else if (window.location.hostname.includes('baidu.com') && settings.searchEngines.baidu) {
            const items = document.querySelectorAll('.result');
            items.forEach(item => {
                const nameElement = item.querySelector('h3 a');
                const linkElement = nameElement ? nameElement.href : '';
                const descElement = item.querySelector('.c-abstract');

                const name = nameElement ? nameElement.innerText.trim() : 'No Title';
                const link = linkElement ? linkElement : '';
                const description = descElement ? descElement.innerText.trim() : 'No Description';

                if (link && isValidURL(link)) results.push({ name, url: link, description });
            });
        }
        // Yandex 검색 결과 추출 (예시)
        else if (window.location.hostname.includes('yandex.com') && settings.searchEngines.yandex) {
            const items = document.querySelectorAll('.serp-item');
            items.forEach(item => {
                const nameElement = item.querySelector('.organic__title-link');
                const linkElement = nameElement ? nameElement.href : '';
                const descElement = item.querySelector('.text-container');

                const name = nameElement ? nameElement.innerText.trim() : 'No Title';
                const link = linkElement ? linkElement : '';
                const description = descElement ? descElement.innerText.trim() : 'No Description';

                if (link && isValidURL(link)) results.push({ name, url: link, description });
            });
        }
        // Ask.com 검색 결과 추출 (추가된 검색 엔진)
        else if (window.location.hostname.includes('ask.com') && settings.searchEngines.ask) {
            const items = document.querySelectorAll('.PartialSearchResults-item');
            items.forEach(item => {
                const nameElement = item.querySelector('h2 a');
                const linkElement = nameElement ? nameElement.href : '';
                const descElement = item.querySelector('.PartialSearchResults-item-abstract');

                const name = nameElement ? nameElement.innerText.trim() : 'No Title';
                const link = linkElement ? linkElement : '';
                const description = descElement ? descElement.innerText.trim() : 'No Description';

                if (link && isValidURL(link)) results.push({ name, url: link, description });
            });
        }
        // Ecosia 검색 결과 추출 (추가된 검색 엔진)
        else if (window.location.hostname.includes('ecosia.org') && settings.searchEngines.ecosia) {
            const items = document.querySelectorAll('.result-item');
            items.forEach(item => {
                const nameElement = item.querySelector('.result-title a');
                const linkElement = nameElement ? nameElement.href : '';
                const descElement = item.querySelector('.result-description');

                const name = nameElement ? nameElement.innerText.trim() : 'No Title';
                const link = linkElement ? linkElement : '';
                const description = descElement ? descElement.innerText.trim() : 'No Description';

                if (link && isValidURL(link)) results.push({ name, url: link, description });
            });
        }
        // Startpage 검색 결과 추출 (추가된 검색 엔진)
        else if (window.location.hostname.includes('startpage.com') && settings.searchEngines.startpage) {
            const items = document.querySelectorAll('.w-gl__result');
            items.forEach(item => {
                const nameElement = item.querySelector('.w-gl__result-title a');
                const linkElement = nameElement ? nameElement.href : '';
                const descElement = item.querySelector('.w-gl__result-text');

                const name = nameElement ? nameElement.innerText.trim() : 'No Title';
                const link = linkElement ? linkElement : '';
                const description = descElement ? descElement.innerText.trim() : 'No Description';

                if (link && isValidURL(link)) results.push({ name, url: link, description });
            });
        }
        // Searx 검색 결과 추출 (추가된 검색 엔진)
        else if (window.location.hostname.includes('searx') && settings.searchEngines.searx) {
            const items = document.querySelectorAll('.result');
            items.forEach(item => {
                const nameElement = item.querySelector('.result_title a');
                const linkElement = nameElement ? nameElement.href : '';
                const descElement = item.querySelector('.result_desc');

                const name = nameElement ? nameElement.innerText.trim() : 'No Title';
                const link = linkElement ? linkElement : '';
                const description = descElement ? descElement.innerText.trim() : 'No Description';

                if (link && isValidURL(link)) results.push({ name, url: link, description });
            });
        }
        // Dogpile 검색 결과 추출 (추가된 검색 엔진)
        else if (window.location.hostname.includes('dogpile.com') && settings.searchEngines.dogpile) {
            const items = document.querySelectorAll('.result_info');
            items.forEach(item => {
                const nameElement = item.querySelector('h3 a');
                const linkElement = nameElement ? nameElement.href : '';
                const descElement = item.querySelector('.result_snippet');

                const name = nameElement ? nameElement.innerText.trim() : 'No Title';
                const link = linkElement ? linkElement : '';
                const description = descElement ? descElement.innerText.trim() : 'No Description';

                if (link && isValidURL(link)) results.push({ name, url: link, description });
            });
        }
        // Bing Images 검색 결과 추출 (추가된 검색 엔진)
        else if (window.location.hostname.includes('bing.com') && settings.searchEngines.bingimages) {
            const items = document.querySelectorAll('.iusc');
            items.forEach(item => {
                const nameElement = item.querySelector('a.iusc');
                const linkElement = nameElement ? nameElement.href : '';
                const descElement = item.querySelector('.txt');

                const name = nameElement ? nameElement.innerText.trim() : 'No Title';
                const link = linkElement ? linkElement : '';
                const description = descElement ? descElement.innerText.trim() : 'No Description';

                if (link && isValidURL(link)) results.push({ name, url: link, description });
            });
        }
        // 기타 검색 엔진에 대한 추가 로직 필요
        else {
            log('지원되지 않거나 비활성화된 검색 엔진입니다.', 'warning');
        }

        return results.length > 0 ? results : null;
    }

    // 다음 페이지 URL 얻는 함수 (각 검색 엔진에 맞게 수정됨)
    function getNextPageUrl() {
        let nextPageUrl = null;
        // 구글 다음 페이지 링크
        if (window.location.hostname.includes('google.com') && settings.searchEngines.google) {
            const nextLink = document.querySelector('a#pnnext');
            if (nextLink) nextPageUrl = nextLink.href;
        }
        // 빙 다음 페이지 링크
        else if (window.location.hostname.includes('bing.com') && settings.searchEngines.bing) {
            const nextLink = document.querySelector('a.sb_pagN');
            if (nextLink) nextPageUrl = nextLink.href;
        }
        // 야후 다음 페이지 링크
        else if (window.location.hostname.includes('search.yahoo.com') && settings.searchEngines.yahoo) {
            const nextLink = document.querySelector('a.next');
            if (nextLink) nextPageUrl = nextLink.href;
        }
        // 네이버 다음 페이지 링크
        else if (window.location.hostname.includes('naver.com') && settings.searchEngines.naver) {
            const nextLink = document.querySelector('.sc_page_inner a.btn_next');
            if (nextLink) nextPageUrl = nextLink.href;
        }
        // 다음 다음 페이지 링크
        else if (window.location.hostname.includes('daum.net') && settings.searchEngines.daum) {
            const nextLink = document.querySelector('.paging a.btn_next');
            if (nextLink) nextPageUrl = nextLink.href;
        }
        // DuckDuckGo 다음 페이지 링크 (예시)
        else if (window.location.hostname.includes('duckduckgo.com') && settings.searchEngines.duckduckgo) {
            const nextLink = document.querySelector('a.js-next-page');
            if (nextLink) nextPageUrl = nextLink.href;
        }
        // Baidu 다음 페이지 링크 (예시)
        else if (window.location.hostname.includes('baidu.com') && settings.searchEngines.baidu) {
            const nextLink = document.querySelector('.n');
            if (nextLink) nextPageUrl = nextLink.href;
        }
        // Yandex 다음 페이지 링크 (예시)
        else if (window.location.hostname.includes('yandex.com') && settings.searchEngines.yandex) {
            const nextLink = document.querySelector('.pager__item_kind_next a');
            if (nextLink) nextPageUrl = nextLink.href;
        }
        // Ask.com 다음 페이지 링크 (추가된 검색 엔진)
        else if (window.location.hostname.includes('ask.com') && settings.searchEngines.ask) {
            const nextLink = document.querySelector('.pagination a.next');
            if (nextLink) nextPageUrl = nextLink.href;
        }
        // Ecosia 다음 페이지 링크 (추가된 검색 엔진)
        else if (window.location.hostname.includes('ecosia.org') && settings.searchEngines.ecosia) {
            const nextLink = document.querySelector('.pagination a.next');
            if (nextLink) nextPageUrl = nextLink.href;
        }
        // Startpage 다음 페이지 링크 (추가된 검색 엔진)
        else if (window.location.hostname.includes('startpage.com') && settings.searchEngines.startpage) {
            const nextLink = document.querySelector('.pagination a.next');
            if (nextLink) nextPageUrl = nextLink.href;
        }
        // Searx 다음 페이지 링크 (추가된 검색 엔진)
        else if (window.location.hostname.includes('searx') && settings.searchEngines.searx) {
            const nextLink = document.querySelector('.pagination a.next');
            if (nextLink) nextPageUrl = nextLink.href;
        }
        // Dogpile 다음 페이지 링크 (추가된 검색 엔진)
        else if (window.location.hostname.includes('dogpile.com') && settings.searchEngines.dogpile) {
            const nextLink = document.querySelector('.pagination a.next');
            if (nextLink) nextPageUrl = nextLink.href;
        }
        // Bing Images 다음 페이지 링크 (추가된 검색 엔진)
        else if (window.location.hostname.includes('bing.com') && settings.searchEngines.bingimages) {
            const nextLink = document.querySelector('a.sb_pagN');
            if (nextLink) nextPageUrl = nextLink.href;
        }
        // 기타 검색 엔진에 대한 추가 로직 필요

        log(`다음 페이지 URL: ${nextPageUrl || '없음'}`, 'info');
        return nextPageUrl;
    }

    // GM_xmlhttpRequest를 Promise로 래핑
    function GM_xmlhttpRequestPromise(url) {
        return new Promise((resolve, reject) => {
            let requestUrl = url;
            if (settings.proxy) {
                // 프록시 요청을 위한 URL 구성
                requestUrl = `${settings.proxy}?url=${encodeURIComponent(url)}`;
            }

            GM_xmlhttpRequest({
                method: "GET",
                url: requestUrl,
                headers: {
                    'User-Agent': getCurrentUserAgent(),
                    'Accept': '*/*'
                },
                timeout: settings.timeout,
                onload: function(response) {
                    if (response.status >= 200 && response.status < 300) {
                        resolve(response);
                    } else {
                        reject(new Error(`HTTP Status ${response.status}`));
                    }
                },
                onerror: function(error) {
                    reject(new Error('Request failed'));
                },
                ontimeout: function() {
                    reject(new Error('Request timed out'));
                }
            });
        });
    }

    // 현재 사용자 에이전트 얻는 함수
    function getCurrentUserAgent() {
        return settings.userAgents[settings.userAgentIndex % settings.userAgents.length];
    }

    // 콘텐츠 유형 얻는 함수
    function getContentType(response) {
        const contentTypeHeader = response.responseHeaders.split('\n').find(header => header.toLowerCase().startsWith('content-type'));
        return contentTypeHeader ? contentTypeHeader.split(':')[1].trim().split(';')[0] : '';
    }

    // 콘텐츠 유형 허용 여부 검사
    function isContentTypeAllowed(contentType) {
        if (settings.contentTypes.include.length > 0 && !settings.contentTypes.include.includes(contentType)) {
            return false;
        }
        if (settings.contentTypes.exclude.length > 0 && settings.contentTypes.exclude.includes(contentType)) {
            return false;
        }
        return true;
    }

    // 콘텐츠 품질 평가 함수
    function evaluateContentQuality(text) {
        const wordCount = text.split(/\s+/).length;
        return wordCount;
    }

    // 키워드 분류 함수 (사용자 지정 키워드 포함)
    function classifyKeywords(text) {
        const standardKeywords = combineKeywords();
        for (const [category, kws] of Object.entries(standardKeywords)) {
            if (kws.some(kw => text.includes(kw))) {
                return category;
            }
        }
        return '기타';
    }

    // 메타 태그 추출 함수
    function extractMetaTags(pageDoc) {
        const metaTags = {};
        const metas = pageDoc.querySelectorAll('meta');
        metas.forEach(meta => {
            const name = meta.getAttribute('name') || meta.getAttribute('property');
            const content = meta.getAttribute('content');
            if (name && content) {
                metaTags[name.toLowerCase()] = content;
            }
        });
        return metaTags;
    }

    // 헤딩 추출 함수
    function extractHeadings(pageDoc) {
        const headings = [];
        for (let i = 1; i <= 6; i++) {
            const hTags = pageDoc.querySelectorAll(`h${i}`);
            hTags.forEach(h => {
                headings.push(h.innerText.trim());
            });
        }
        return headings;
    }

    // URL 유효성 검사 함수
    function isValidURL(url) {
        try {
            new URL(url);
            return true;
        } catch (_) {
            return false;
        }
    }

    // robots.txt 확인 함수
    async function checkRobotsTxt(url) {
        try {
            const parsedUrl = new URL(url);
            const robotsUrl = `${parsedUrl.origin}/robots.txt`;
            const response = await GM_xmlhttpRequestPromise(robotsUrl);
            const robotsText = response.responseText;
            const parser = new RobotsParser(robotsUrl, robotsText);
            return parser.isAllowed(url, getCurrentUserAgent());
        } catch (error) {
            log(`robots.txt 확인 실패: ${error.message}`, 'warning');
            // robots.txt를 가져올 수 없으면 기본적으로 허용
            return true;
        }
    }

    // robots-parser 라이브러리 (간단한 구현)
    class RobotsParser {
        constructor(url, text) {
            this.rules = [];
            this.parse(text);
        }

        parse(text) {
            const lines = text.split('\n');
            let userAgents = [];
            for (let line of lines) {
                line = line.trim();
                if (line.startsWith('#') || line === '') continue;
                const parts = line.split(':');
                if (parts.length < 2) continue;
                const key = parts[0].trim().toLowerCase();
                const value = parts.slice(1).join(':').trim();
                if (key === 'user-agent') {
                    userAgents = [value.toLowerCase()];
                } else if (key === 'disallow') {
                    this.rules.push({ userAgents: [...userAgents], disallow: value });
                } else if (key === 'allow') {
                    this.rules.push({ userAgents: [...userAgents], allow: value });
                }
            }
        }

        isAllowed(url, ua) {
            const path = new URL(url).pathname;
            let allowed = true;
            for (let rule of this.rules) {
                if (rule.userAgents.includes('*') || rule.userAgents.includes(ua.toLowerCase())) {
                    if (rule.disallow && path.startsWith(rule.disallow)) {
                        allowed = false;
                    }
                    if (rule.allow && path.startsWith(rule.allow)) {
                        allowed = true;
                    }
                }
            }
            return allowed;
        }
    }

    // 큐에 있는 요청을 처리하는 딜레이 함수
    async function processQueueWithDelay() {
        if (requestQueue.length > 0 && activeRequests < settings.maxConcurrentRequests) {
            const { url, depth } = requestQueue.shift();
            activeRequests++;
            try {
                await handleRequest(url, depth);
            } catch (error) {
                log(`요청 처리 중 오류 발생: ${url} - ${error.message}`, 'bug');
            } finally {
                activeRequests--;
                processQueueWithDelay();
            }
        }
    }

    // 딜레이 함수
    function delay(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

    // 모달 창 기능 이미 구현됨

    // 추가적인 기능: HTML 내용 수집 및 텍스트 추출, 데이터 테이블에 표시, 모달 창으로 HTML 보기
    // 이미 기존 스크립트에서 구현됨

    function init() {
        createUI();
        if (isCrawling) {
            log('크롤링이 이전에 중단된 상태에서 다시 시작됩니다.', 'info');
            setButtonStates();
            crawlPage(currentDepth); // 페이지 로드 후 크롤링 재개
        } else {
            log('크롤러가 초기화되었습니다.', 'info');
        }
    }

    // 페이지 로드 시 초기화
    window.addEventListener('load', init);

    // 크롤링 한 사이트 저장 및 관리 추가 기능
    // 이미 UI에 추가됨

})();             
     
   설정 쪽  다양하면서도 세심하게 만드셈 그리고 다양한 기능들을 엄청 많이 추가하셈 굉장히 실용적이면서도 참신해야 함 

크롤링하는 사이트를 더더 확실하게 할 수 있는 방법들을 추가하셈 또 데이터쪽  굉장히 세심하면서도 다양한 기능들 많이 엄청 추가하셈. 굉장히 실용적이면서도 참신한 걸로  하지만 본질적인 거는 남겨놓고 ㅇㅇ 그리고 검색 엔진 다양하게 하셈 



그리고 맨위에 좋합 로그 놓고 버그나 밑에 그런 것들 로그들 다 따로따로 놓고  걍 모든 로그 나누고 맨 위에 좋합 로그 넣어두셈

그리고 종합로그나 버그 그런 것들 로그에서  버그나 그런 것들은 로그 메세지를 빨간 색 

또 크롤링 한 사이트 저장하고 또 그 저장한 사이트 들중에서 뺴고 넣고 이런 기능 추가        생략하지 말고 빠르고 정확하게 만들어. 알겟지? 생략하지 말고 빠르고 정확하게 ㅇㅇ