import subprocess
import sys
import os
import base64
import mimetypes
import time
import random
import re
import threading
import tkinter as tk
from tkinter import ttk, messagebox, scrolledtext
import tkinter.font as tkfont
import urllib3
import requests
from urllib.parse import urlparse, urljoin, unquote
from bs4 import BeautifulSoup
import undetected_chromedriver as uc
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

# ==================== ORIGINAL SCRAPING CODE (UNMODIFIED) ====================
# All scraping functions are copied exactly as provided, with only minimal
# changes to make archive_thread accept a URL parameter instead of input().

def _parse_chrome_major_from_version_text(text: str):
    m = re.search(r'(\d+)\.(\d+)\.(\d+)\.(\d+)', text)
    if not m:
        m = re.search(r'Chrome\s+(\d+)\.', text, re.IGNORECASE)
    if not m:
        return None
    return int(m.group(1))

def get_chrome_major_version():
    try:
        if sys.platform == "win32":
            import winreg
            uninstall_roots = [
                r"SOFTWARE\Google\Chrome",
                r"SOFTWARE\WOW6432Node\Google\Chrome",
            ]
            for root_path in uninstall_roots:
                try:
                    key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, root_path + r"\BLBeacon")
                    val = winreg.QueryValueEx(key, "version")[0]
                    major = _parse_chrome_major_from_version_text(val)
                    if major:
                        return major
                except Exception:
                    pass

        candidates = []
        if sys.platform == "win32":
            candidates += [
                r"C:\Program Files\Google\Chrome\Application\chrome.exe",
                r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe",
                "chrome.exe", "GoogleChrome.exe", "chrome"
            ]
        elif sys.platform == "darwin":
            candidates += [
                "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
                "/Applications/Chromium.app/Contents/MacOS/Chromium",
                "google-chrome", "chromium",
            ]
        else:
            candidates += ["google-chrome", "google-chrome-stable", "chromium-browser", "chromium"]

        for cmd in candidates:
            try:
                if os.path.isabs(cmd) and not os.path.exists(cmd):
                    continue
                p = subprocess.run([cmd, "--version"], capture_output=True, text=True, check=False)
                out = (p.stdout or "") + (p.stderr or "")
                major = _parse_chrome_major_from_version_text(out.strip())
                if major:
                    return major
            except Exception:
                pass
    except Exception:
        pass
    return None

def make_driver(PROFILE_DIR):
    options_uc = uc.ChromeOptions()
    options_uc.add_argument(f"--user-data-dir={PROFILE_DIR}")
    major = get_chrome_major_version()
    print(f"Detected Chrome major version: {major}")
    try_versions = [major] if major is not None else [None]
    last_exc = None
    for vmain in try_versions:
        try:
            if vmain is None:
                return uc.Chrome(options=options_uc, headless=False, use_subprocess=True)
            return uc.Chrome(options=options_uc, headless=False, use_subprocess=True, version_main=vmain)
        except Exception as e:
            last_exc = e
            msg = str(e)
            m = re.search(r"only supports Chrome version\s+(\d+)", msg, re.IGNORECASE)
            if m:
                supported_major = int(m.group(1))
                try:
                    return uc.Chrome(options=options_uc, headless=False, use_subprocess=True, version_main=supported_major)
                except Exception as e2:
                    last_exc = e2
    raise last_exc

FALLBACK_URL = "https://soyjak.forum"
ATTACH_HREF_REGEX = re.compile(r'/(attachments|attachment)/', re.IGNORECASE)
COMMON_FILE_EXTS = {".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp", ".mp4", ".webm", ".ogg", ".mp3", ".wav", ".m4a", ".pdf", ".zip", ".rar", ".7z", ".tar", ".gz", ".txt", ".csv", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".svg"}

def sanitize_filename(url):
    parsed = urlparse(url)
    path = unquote(parsed.path)
    filename = os.path.basename(path)
    filename = re.sub(r'\?.*$', '', filename)
    filename = re.sub(r'[^\w\-_.]', '_', filename)
    if not filename or len(filename) > 200:
        filename = f"asset_{int(time.time())}_{random.randint(1000,9999)}"
    if '.' not in filename:
        ext = os.path.splitext(path)[1].lower()
        if ext:
            filename += ext
    return filename

def normalize_url(url):
    if not url or url.startswith("data:"):
        return url
    if url.startswith("//"):
        return "https:" + url
    if url.startswith("/") or url.startswith("data/") or url.startswith("styles/") or url.startswith("attachments/"):
        return urljoin(BASE_DOMAIN, url)
    return url

def parse_srcset(srcset_value):
    if not srcset_value:
        return []
    parts = [p.strip() for p in srcset_value.split(",") if p.strip()]
    results = []
    for part in parts:
        tokens = part.split()
        url = tokens[0].strip()
        desc = tokens[1].strip() if len(tokens) > 1 else None
        results.append((url, desc))
    return results

def pick_best_srcset_candidate(srcset_value):
    candidates = parse_srcset(srcset_value)
    if not candidates:
        return None
    def score(desc):
        if not desc:
            return 0
        m = re.match(r'(\d+(?:\.\d+)?)(x|w)$', desc)
        if not m:
            return 0
        return float(m.group(1))
    best_url, _ = max(candidates, key=lambda c: score(c[1]))
    return best_url

def build_srcset_with_local_urls(img_tag, attr_name, session, referer):
    rewritten_any = False
    original = img_tag.get(attr_name)
    if not original:
        return False
    candidates = parse_srcset(original)
    if not candidates:
        return False
    new_parts = []
    for url, desc in candidates:
        url_norm = normalize_url(url)
        if not url_norm or not url_norm.startswith("http"):
            new_parts.append(f"{url} {desc}".strip())
            continue
        local = download_asset(session, url_norm, ATTACH_DIR, referer=referer)
        if local != url_norm:
            rewritten_any = True
        if desc:
            new_parts.append(f"{local} {desc}")
        else:
            new_parts.append(local)
    img_tag[attr_name] = ", ".join(new_parts)
    return rewritten_any

def get_img_candidate_urls(img_tag):
    urls = []
    for attr in ("src", "data-src"):
        v = img_tag.get(attr)
        if v:
            urls.append(v)
    for attr in ("srcset", "data-srcset"):
        v = img_tag.get(attr)
        if v:
            candidates = parse_srcset(v)
            for u, _desc in candidates:
                urls.append(u)
    out = []
    for u in urls:
        nu = normalize_url(u)
        if nu and nu != "" and not nu.startswith("data:"):
            out.append(nu)
    return out

def setup_requests_session(driver):
    session = requests.Session()
    try:
        ua = driver.execute_script("return navigator.userAgent;")
    except Exception:
        ua = "Mozilla/5.0 (compatible; ThreadArchiver/1.0)"
    session.headers.update({
        "User-Agent": ua,
        "Referer": BASE_DOMAIN,
        "Accept": "*/*",
        "Accept-Language": "en-US,en;q=0.9",
        "Connection": "keep-alive"
    })
    for cookie in driver.get_cookies():
        name = cookie.get("name")
        value = cookie.get("value")
        if not name:
            continue
        domain = cookie.get("domain") or HOST
        path = cookie.get("path") or "/"
        domain = domain.lstrip(".") if isinstance(domain, str) else HOST
        session.cookies.set(name, value, domain=domain, path=path)
    return session

def download_asset(session, url, target_folder, referer=None):
    original = url
    url = normalize_url(url)
    if not url or url.startswith("data:") or not url.startswith("http"):
        return original
    local_name = sanitize_filename(url)
    dest = os.path.join(target_folder, local_name)
    if os.path.exists(dest):
        return os.path.relpath(dest, OUTPUT_DIR).replace("\\", "/")
    headers = {"Referer": referer or BASE_DOMAIN}
    for attempt in range(MAX_RETRIES ):
        try:
            resp = session.get(url, headers=headers, timeout=REQUEST_TIMEOUT, stream=True, verify=False)
            if resp.status_code == 200:
                os.makedirs(os.path.dirname(dest), exist_ok=True)
                with open(dest, "wb") as f:
                    for chunk in resp.iter_content(8192):
                        if chunk:
                            f.write(chunk)
                print(f"    ✓ {local_name}")
                return os.path.relpath(dest, OUTPUT_DIR).replace("\\", "/")
            if resp.status_code in (403, 429):
                wait = (BACKOFF_BASE ** attempt) + random.random()
                print(f"    … HTTP {resp.status_code} retry {attempt}/{MAX_RETRIES} in {wait:.1f}s")
                time.sleep(wait)
                continue
            print(f"    ✗ HTTP {resp.status_code}: {original}")
            return original
        except Exception as e:
            if attempt >= MAX_RETRIES:
                print(f"    ✗ Exception: {original}")
                return original
            wait = (BACKOFF_BASE ** attempt) + random.random()
            print(f"    … Exception, retry in {wait:.1f}s")
            time.sleep(wait)
    return original

def extract_reactions(post):
    reactions_bar = post.select_one(".reactionsBar, .js-reactionsList")
    if not reactions_bar:
        return ""
    
    reaction_items = reactions_bar.select("li .reaction, .reactionSummary .reaction")
    reaction_details = []
    for item in reaction_items:
        react_span = item if item.name == "span" else item.find_parent("span")
        if not react_span:
            continue
        img = react_span.find("img")
        alt = img.get("alt") or img.get("title") if img else ""
        title = react_span.get("title") or alt
        if title:
            reaction_details.append(title)
    
    user_link = reactions_bar.select_one(".reactionsBar-link")
    users_text = ""
    if user_link:
        users_text = user_link.get_text(strip=True)
    else:
        bdis = reactions_bar.select("bdi")
        if bdis:
            users_text = ", ".join(bd.get_text(strip=True) for bd in bdis)
    
    if not reaction_details and not users_text:
        return ""
    
    if reaction_details:
        reacts_str = " | ".join(reaction_details)
        display = f"{users_text} reacted with: {reacts_str}"
    else:
        display = users_text
    
    reactions_html = f"""
    <div class="reactions-bar">
        <strong>Reactions:</strong> {display}
    </div>
    """
    return reactions_html

def create_clean_layout(soup, page_number, total_pages=892):
    title_tag = soup.find("h1", class_="p-title-value") or soup.find("title")
    title_text = title_tag.get_text(strip=True) if title_tag else "Soyjak Forum"
    prev_html = f'<a href="page_{page_number-1}.html">← Previous</a>' if page_number > 1 else ''
    next_html = f'<a href="page_{page_number+1}.html">Next →</a>' if page_number < total_pages else ''
    ENABLE_REPLACEMENTS = True
    REPL_LIAR = "kike"
    REPL_NOOB = "nigger"
    REPL_BAD_DRIVER = "chink"
    REPL_CACA = "fag"
    REPL_CACAOT="faggot"
    REPL_REAL_WOMAN = "tranny"
    term_replacements = [
        (re.compile(r"\bliar\b", re.IGNORECASE), REPL_LIAR),
        (re.compile(r"\bnoob\b", re.IGNORECASE), REPL_NOOB),
        (re.compile(r"\bbad\s+driver\b", re.IGNORECASE), REPL_BAD_DRIVER),
        (re.compile(r"\bcaca\b", re.IGNORECASE), REPL_CACA),
        (re.compile(r"\bcacaot\b", re.IGNORECASE), REPL_CACA),
        (re.compile(r"\breal\s+woman\b", re.IGNORECASE), REPL_REAL_WOMAN),
    ]
    def replace_terms(text: str) -> str:
        if not text or not ENABLE_REPLACEMENTS:
            return text
        out = text
        for pattern, repl in term_replacements:
            out = pattern.sub(repl, out)
        return out
    new_html = f"""
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="utf-8">
        <title>{title_text} - Page {page_number}</title>
        <style>
            body {{ font-family: Arial, sans-serif; margin: 0; padding: 15px; background: #f4f4f4; line-height: 1.5; }}
            .header {{ background: #2c3e50; color: white; padding: 15px; margin-bottom: 20px; border-radius: 8px; }}
            .message {{ background: white; border: 1px solid #ddd; border-radius: 8px; margin-bottom: 20px; padding: 15px; display: flex; gap: 15px; }}
            .avatar {{ flex-shrink: 0; width: 75px; }}
            .avatar img {{ width: 75px; height: 75px; border-radius: 8px; object-fit: cover; }}
            .message-body {{ flex: 1; min-width: 0; }}
            .username {{ font-weight: bold; font-size: 1.15em; margin-bottom: 4px; }}
            .badges img {{ max-height: 28px; margin: 2px 4px 2px 0; vertical-align: middle; }}
            .content img, .content video, .content audio {{ max-width: 100%; height: auto; display: block; margin: 12px 0; border-radius: 6px; }}
            .reactions-bar {{
                margin-top: 12px;
                padding: 8px 12px;
                background: #f8f9fa;
                border-top: 1px solid #eee;
                border-radius: 4px;
                font-size: 0.92em;
                color: #555;
            }}
            .reactions-bar strong {{ color: #333; }}
            .pagination {{ text-align: center; margin: 40px 0; padding: 15px; background: white; border-radius: 8px; }}
            .pagination a, .pagination button {{ padding: 8px 16px; margin: 0 5px; background: #3498db; color: white; text-decoration: none; border: none; border-radius: 4px; cursor: pointer; }}
        </style>
    </head>
    <body>
        <div class="header">
            <h1>{title_text}</h1>
            <p>Page {page_number}</p>
        </div>
    """
    for post in soup.find_all("article", class_="message"):
        username_tag = post.find("a", class_="username") or post.find(".username") or post.find(".message-name")
        username = username_tag.get_text(strip=True) if username_tag else "Unknown"
        username = replace_terms(username)
        avatar = None
        for selector in ["img.avatar", "a.avatar img", "img[data-user-id]", "img.avatar-u", ".message-avatar img"]:
            avatar = post.select_one(selector)
            if avatar and avatar.get("src"):
                break
        avatar_src = avatar.get("src", "") if avatar else ""
        badges = post.find("div", class_="featuredBadges")
        badges_html = str(badges) if badges else ""
        badges_html = replace_terms(badges_html)
        content = post.find("div", class_="message-content") or post.find("article") or post
        content_html = str(content) if content else "<p>No content</p>"
        content_html = replace_terms(content_html)
        reactions_html = extract_reactions(post)
        new_html += f"""
        <div class="message">
            <div class="avatar">
                <img src="{avatar_src}" alt="avatar">
            </div>
            <div class="message-body">
                <div class="username">{username}</div>
                <div class="badges">{badges_html}</div>
                <div class="content">{content_html}</div>
                {reactions_html}
            </div>
        </div>
        """
    new_html += f"""
        <div class="pagination">
            {prev_html}
            <strong>Page {page_number}</strong>
            {next_html}
            <div style="margin-top:15px;">
                <input type="number" id="pageInput" placeholder="Go to page" min="1" max="{total_pages}" style="padding:8px; width:130px;">
                <button onclick="goToPage()">Go</button>
            </div>
        </div>
        <script>
            function goToPage() {{
                let page = document.getElementById('pageInput').value.trim();
                if (page) window.location.href = `page_${{page}}.html`;
            }}
        </script>
    </body></html>
    """
    return BeautifulSoup(new_html, "html.parser")

def rewrite_images_and_media(clean_soup, session, referer):
    for img in clean_soup.find_all("img"):
        best_srcset_url = pick_best_srcset_candidate(img.get("srcset")) or pick_best_srcset_candidate(img.get("data-srcset"))
        candidates = []
        for attr in ("src", "data-src"):
            v = img.get(attr)
            if v:
                candidates.append(normalize_url(v))
        if best_srcset_url:
            candidates.append(normalize_url(best_srcset_url))
        primary_url = None
        for c in candidates:
            if c and c.startswith("http"):
                primary_url = c
                break
        if primary_url:
            local = download_asset(session, primary_url, ATTACH_DIR, referer=referer)
            if local:
                if img.get("src") is not None:
                    img["src"] = local
                if img.get("data-src") is not None:
                    img["data-src"] = local
        try:
            if img.get("srcset"):
                build_srcset_with_local_urls(img, "srcset", session, referer)
            if img.get("data-srcset"):
                build_srcset_with_local_urls(img, "data-srcset", session, referer)
        except Exception:
            pass

    for media in clean_soup.find_all(["video", "audio"]):
        if media.get("src"):
            local = download_asset(session, media["src"], ATTACH_DIR, referer=referer)
            if local:
                media["src"] = local
        for source in media.find_all("source"):
            if source.get("src"):
                local = download_asset(session, source["src"], ATTACH_DIR, referer=referer)
                if local:
                    source["src"] = local

    # Sprite handling (kept exactly as original)
    try:
        sprite_classes = set()
        for img in clean_soup.find_all("img"):
            cls = img.get("class") or []
            for c in cls:
                if isinstance(c, str) and c.startswith("smilie--sprite") and c != "smilie--sprite":
                    sprite_classes.add(c)
        if sprite_classes:
            css_urls = []
            for link in clean_soup.find_all("link", rel=True):
                if link.get("rel") and "stylesheet" in link.get("rel"):
                    href = link.get("href")
                    if href:
                        css_urls.append(normalize_url(href))
            rule_block_re = re.compile(r"\.(smilie--sprite\d+)\s*\{([^}]*)\}", re.IGNORECASE | re.DOTALL)
            def grab_prop(block: str, prop_name: str):
                m = re.search(rf"{re.escape(prop_name)}\s*:\s*([^;]+);", block, flags=re.IGNORECASE)
                return m.group(1).strip() if m else None
            sprite_to_props = {}
            for css_url in css_urls:
                try:
                    resp = session.get(css_url, headers={"Referer": referer}, timeout=30)
                    if resp.status_code != 200:
                        continue
                    css_text = resp.text
                    for m in rule_block_re.finditer(css_text):
                        sprite_class = m.group(1)
                        if sprite_class not in sprite_classes:
                            continue
                        block = m.group(2)
                        bg_img = grab_prop(block, "background-image")
                        bg_img_url = None
                        if bg_img:
                            u = re.search(r"url\(\s*['\"]?([^'\")]+)['\"]?\s*\)", bg_img, flags=re.IGNORECASE)
                            if u:
                                bg_img_url = u.group(1).strip()
                        props = {
                            "background-image": bg_img_url,
                            "background-position": grab_prop(block, "background-position"),
                            "background-repeat": grab_prop(block, "background-repeat"),
                            "width": grab_prop(block, "width"),
                            "height": grab_prop(block, "height"),
                            "background-size": grab_prop(block, "background-size"),
                        }
                        if props["background-image"] or (props["background-position"] and props["width"] and props["height"]):
                            sprite_to_props[sprite_class] = props
                except Exception:
                    pass
            downloaded = {}
            override_lines = []
            for sprite_class, props in sprite_to_props.items():
                remote_sprite_url = props.get("background-image")
                if not remote_sprite_url:
                    continue
                remote_sprite_url = normalize_url(remote_sprite_url)
                if remote_sprite_url and remote_sprite_url not in downloaded:
                    local_sprite = download_asset(session, remote_sprite_url, ATTACH_DIR, referer=referer)
                    if local_sprite:
                        downloaded[remote_sprite_url] = local_sprite
                local_sprite_url = downloaded.get(remote_sprite_url)
                if not local_sprite_url:
                    continue
                width = props.get("width")
                height = props.get("height")
                bpos = props.get("background-position")
                brep = props.get("background-repeat")
                bsize = props.get("background-size")
                line = f".{sprite_class}{{background-image:url('{local_sprite_url}')}}"
                if bpos:
                    line += f";background-position:{bpos}"
                line += ";"
                if brep:
                    line += f"background-repeat:{brep};"
                else:
                    line += "background-repeat:no-repeat;"
                if width:
                    line += f"width:{width};"
                if height:
                    line += f"height:{height};"
                if bsize:
                    line += f"background-size:{bsize};"
                line += "display:inline-block;}\n"
                override_lines.append(line)
            if override_lines:
                style_tag = clean_soup.new_tag("style")
                style_tag.string = "".join(override_lines)
                head = clean_soup.find("head")
                if head:
                    head.append(style_tag)
                else:
                    clean_soup.append(style_tag)
    except Exception:
        pass
    return clean_soup

def rewrite_attachment_links(clean_soup, session, referer):
    for a in clean_soup.find_all("a", href=True):
        href = a.get("href")
        if not href:
            continue
        href_norm = normalize_url(href)
        if not href_norm or not href_norm.startswith("http"):
            continue
        parsed = urlparse(href_norm)
        path = parsed.path.lower()
        ext = os.path.splitext(path)[1] if "." in path else ""
        looks_like_attachment = bool(ATTACH_HREF_REGEX.search(path)) or any(ext == e for e in COMMON_FILE_EXTS)
        if not looks_like_attachment:
            continue
        local = download_asset(session, href_norm, ATTACH_DIR, referer=referer)
        if local and local != href_norm:
            a["href"] = local
    return clean_soup

# Global variables used by scraping functions
BASE_DOMAIN = None
HOST = None
OUTPUT_DIR = None
ATTACH_DIR = None
PROFILE_DIR = r".\chrome_profile"
MAX_RETRIES = 1
REQUEST_TIMEOUT = 20
BACKOFF_BASE = 1.7
ENABLE_REPLACEMENTS = True   # will be controlled by the GUI toggle

def get_highest_existing_page(output_dir):
    """Return the highest page_N.html number that already exists, or 0 if none."""
    if not os.path.isdir(output_dir):
        return 0
    max_page = 0
    for name in os.listdir(output_dir):
        m = re.match(r'^page_(\d+)\.html$', name, re.IGNORECASE)
        if m:
            try:
                n = int(m.group(1))
                if n > max_page:
                    max_page = n
            except ValueError:
                pass
    return max_page

def build_page_url_from_current(current_url, page_number):
    """
    Take the live URL the browser is on and turn it into the desired page URL.
    Handles both /page-N and clean first-page URLs.
    """
    url = current_url.split('#')[0].rstrip('/')
    # Strip any existing /page-N
    url = re.sub(r'/page-\d+$', '', url, flags=re.IGNORECASE)
    if page_number <= 1:
        return url
    return f"{url}/page-{page_number}"

def archive_thread(target_url):
    global BASE_DOMAIN, HOST, OUTPUT_DIR, ATTACH_DIR, ENABLE_REPLACEMENTS
    user_input = target_url.strip()
    TARGET_URL = user_input if user_input else FALLBACK_URL
    parsed_target = urlparse(TARGET_URL)
    BASE_DOMAIN = f"{parsed_target.scheme}://{parsed_target.netloc}"
    HOST = parsed_target.netloc
    thread_match = re.search(r'threads/([^/]+)', parsed_target.path)
    folder_suffix = thread_match.group(1) if thread_match else f"thread_{int(time.time())}"
    OUTPUT_DIR = os.path.join(".", f"{folder_suffix}")
    ATTACH_DIR = os.path.join(OUTPUT_DIR, "Attachments")
    for d in [OUTPUT_DIR, ATTACH_DIR]:
        os.makedirs(d, exist_ok=True)

    highest = get_highest_existing_page(OUTPUT_DIR)
    start_page = highest + 1
    if highest > 0:
        print(f"Found existing pages up to page_{highest}.html → will resume from page {start_page}")
    else:
        print("No existing pages found. Starting from page 1.")

    print("Launching browser...")
    driver = make_driver(PROFILE_DIR)
    try:
        # ===== Original browser start sequence (kept intact) =====
        driver.get(BASE_DOMAIN)
        time.sleep(5)

        # Always load the user-supplied first-page URL first
        # (this establishes cookies / Cloudflare / session the same way as before)
        print(f"Loading thread start URL: {TARGET_URL}")
        driver.get(TARGET_URL)
        WebDriverWait(driver, 25).until(EC.presence_of_element_located((By.CLASS_NAME, "p-pageWrapper")))
        time.sleep(3)

        # Now decide where to actually begin scraping
        if start_page > 1:
            # Build the resume URL from the *real* URL the browser is currently on
            live_url = driver.current_url
            current_url = build_page_url_from_current(live_url, start_page)
            page_number = start_page
            print(f"Navigating to resume point: {current_url}")
            driver.get(current_url)
            WebDriverWait(driver, 25).until(EC.presence_of_element_located((By.CLASS_NAME, "p-pageWrapper")))
            time.sleep(3)
        else:
            current_url = TARGET_URL
            page_number = 1

        total_pages = 9999

        while current_url:
            print(f"\n=== Scraping Page {page_number} ===")
            # (When we just jumped for resume, the page is already loaded;
            #  subsequent iterations will driver.get() normally)
            if page_number != start_page or start_page == 1:
                driver.get(current_url)
                WebDriverWait(driver, 25).until(EC.presence_of_element_located((By.CLASS_NAME, "p-pageWrapper")))
                time.sleep(4)

            # Original scroll-to-load behaviour
            last = driver.execute_script("return document.body.scrollHeight")
            for _ in range(40):
                driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
                time.sleep(1.5)
                new = driver.execute_script("return document.body.scrollHeight")
                if new == last:
                    break
                last = new

            session = setup_requests_session(driver)
            referer = current_url
            soup = BeautifulSoup(driver.page_source, "html.parser")

            print("Building clean layout...")
            clean_soup = create_clean_layout(soup, page_number, total_pages=total_pages)

            print("Rewriting and downloading images/media...")
            clean_soup = rewrite_images_and_media(clean_soup, session, referer)

            print("Rewriting and downloading attachment links...")
            clean_soup = rewrite_attachment_links(clean_soup, session, referer)

            file_path = os.path.join(OUTPUT_DIR, f"page_{page_number}.html")
            with open(file_path, "w", encoding="utf-8") as f:
                f.write(str(clean_soup))
            print(f"✓ Saved page_{page_number}.html")

            # Original next-page logic
            try:
                next_btn = driver.find_element(By.CSS_SELECTOR, "a.pageNav-jump--next")
                next_url = next_btn.get_attribute("href")
                current_url = urljoin(BASE_DOMAIN, next_url) if next_url else None
                page_number += 1
                time.sleep(3)
            except Exception:
                print("✅ End of thread reached.")
                current_url = None
    finally:
        driver.quit()
        print("\nDone with this thread!")

# ==================== GUI CODE ====================

class ConsoleRedirector:
    def __init__(self, text_widget):
        self.text_widget = text_widget

    def write(self, string):
        if self.text_widget:
            self.text_widget.config(state="normal")
            self.text_widget.insert("end", string)
            self.text_widget.see("end")
            self.text_widget.config(state="disabled")

    def flush(self):
        pass


class ThreadArchiverGUI:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("Shroomchiver GUI - Multi-URL")
        self.root.geometry("900x700")
        self.root.configure(bg="#f0f0f0")
        self.root.attributes("-topmost", True)
        
        self.title_font = ("Arial", 14, "bold")
        self.normal_font = ("Arial", 9)
        self.console_font = ("Courier New", 9)
        
        # Toggle for term replacements
        self.enable_replacements = tk.BooleanVar(value=True)
        
        # Main frame
        main_frame = tk.Frame(self.root, bg="#f0f0f0")
        main_frame.pack(fill="both", expand=True, padx=8, pady=8)
        
        # Left side - Controls
        left_frame = tk.Frame(main_frame, bg="#f0f0f0", width=420)
        left_frame.pack(side="left", fill="y", padx=(0, 8))
        left_frame.pack_propagate(False)
        
        title_label = tk.Label(left_frame, text="Shroomchiver GUI (Multi)", 
                              font=self.title_font, bg="#f0f0f0", fg="#003399")
        title_label.pack(pady=(0, 8))
        
        tk.Label(left_frame, text="Enter thread URLs (one per line):", 
                bg="#f0f0f0", anchor="w", font=self.normal_font).pack(anchor="w", pady=(0, 4))
        
        # Multi-line URL input
        self.url_text = scrolledtext.ScrolledText(left_frame, font=self.normal_font, 
                                                  width=55, height=10, wrap="none")
        self.url_text.pack(pady=(0, 10), fill="x")
        
        # Toggle for replacements
        toggle_frame = tk.Frame(left_frame, bg="#f0f0f0")
        toggle_frame.pack(anchor="w", pady=4)
        self.toggle_check = tk.Checkbutton(toggle_frame, text="Enable wordfilter bypass (liar → kike, etc.)",
                                         variable=self.enable_replacements, bg="#f0f0f0",
                                         font=self.normal_font, anchor="w")
        self.toggle_check.pack(anchor="w")
        
        tk.Label(left_frame, text="Progress:", bg="#f0f0f0", anchor="w", 
                font=self.normal_font).pack(anchor="w", pady=(10, 4))
        
        self.progress = ttk.Progressbar(left_frame, orient="horizontal", length=380, mode="determinate")
        self.progress.pack(pady=(0, 6))
        
        self.status_label = tk.Label(left_frame, text="Paste one or more URLs and click Accept", 
                                   bg="#f0f0f0", anchor="w", wraplength=380, justify="left",
                                   font=self.normal_font)
        self.status_label.pack(anchor="w")
        
        # Buttons
        btn_frame = tk.Frame(left_frame, bg="#f0f0f0")
        btn_frame.pack(pady=18)
        
        self.accept_btn = tk.Button(btn_frame, text="Accept", width=12, height=1,
                                  relief="raised", bg="#e1e1e1", activebackground="#c0c0c0",
                                  font=("Arial", 9, "bold"), command=self.start_scraping)
        self.accept_btn.pack(side="left", padx=8)
        
        self.exit_btn = tk.Button(btn_frame, text="Exit", width=12, height=1,
                                relief="raised", bg="#e1e1e1", activebackground="#c0c0c0",
                                font=("Arial", 9, "bold"), command=self.root.quit)
        self.exit_btn.pack(side="left", padx=8)
        
        # Right side - Console
        right_frame = tk.Frame(main_frame, bg="#000000", width=450)
        right_frame.pack(side="right", fill="both", expand=True)
        right_frame.pack_propagate(False)
        
        console_label = tk.Label(right_frame, text=" Console Output ", bg="#000000", fg="#00ff00",
                               font=("Arial", 9, "bold"))
        console_label.pack(anchor="w", padx=4, pady=2)
        
        self.console_text = tk.Text(right_frame, bg="#000000", fg="#00ff00", 
                                  font=self.console_font, wrap="word", state="disabled")
        self.console_text.pack(fill="both", expand=True, padx=4, pady=(0, 4))
        
        scrollbar = tk.Scrollbar(right_frame, command=self.console_text.yview)
        scrollbar.pack(side="right", fill="y")
        self.console_text.config(yscrollcommand=scrollbar.set)
        
        # Redirect console
        self.console_redirector = ConsoleRedirector(self.console_text)
        sys.stdout = self.console_redirector
        sys.stderr = self.console_redirector
        
        self.is_running = False
        self._original_print = print

    def update_status(self, text, progress_value=None):
        self.status_label.config(text=text)
        if progress_value is not None:
            self.progress['value'] = progress_value
        self.root.update_idletasks()

    def reset_ui(self):
        self.progress['value'] = 0
        self.status_label.config(text="Paste one or more URLs and click Accept")
        self.url_text.config(state="normal")
        self.accept_btn.config(state="normal")
        self.is_running = False
        try:
            if sys.platform == "win32":
                import winsound
                winsound.MessageBeep(winsound.MB_OK)
        except:
            pass
        messagebox.showinfo("Shroomchiver GUI", "All threads finished!")

    def scraping_worker(self, urls):
        try:
            global ENABLE_REPLACEMENTS
            ENABLE_REPLACEMENTS = self.enable_replacements.get()
            
            total = len(urls)
            for idx, url in enumerate(urls, 1):
                print(f"\n{'='*60}")
                print(f"Starting thread {idx}/{total}: {url}")
                print(f"{'='*60}\n")
                
                self.root.after(0, self.update_status, 
                               f"Thread {idx}/{total}: {url[:60]}...", 
                               int((idx-1)/total * 100))
                
                archive_thread(url)
                
                self.root.after(0, self.update_status, 
                               f"Finished {idx}/{total}", 
                               int(idx/total * 100))
            
            self.root.after(0, self.reset_ui)
        except Exception as e:
            self._original_print(f"ERROR: {e}")
            self.root.after(0, lambda: messagebox.showerror("Error", str(e)))
            self.root.after(0, self.reset_ui)

    def start_scraping(self):
        if self.is_running:
            return
            
        raw = self.url_text.get("1.0", "end").strip()
        if not raw:
            messagebox.showwarning("No URLs", "Please enter at least one thread URL.")
            return
            
        # Split by lines and clean
        urls = []
        for line in raw.splitlines():
            line = line.strip()
            if line and line.startswith("http"):
                urls.append(line)
                
        if not urls:
            messagebox.showwarning("Invalid URLs", "No valid http/https URLs found.")
            return
        
        self.is_running = True
        self.url_text.config(state="disabled")
        self.accept_btn.config(state="disabled")
        self.update_status(f"Starting {len(urls)} thread(s)...", 2)
        
        thread = threading.Thread(target=self.scraping_worker, args=(urls,), daemon=True)
        thread.start()

if __name__ == "__main__":
    app = ThreadArchiverGUI()
    app.root.mainloop()