import sys
import sqlite3
from urllib.parse import urljoin, urlparse
import requests
from bs4 import BeautifulSoup

from PySide6.QtCore import Qt, QThread, Signal, QUrl
from PySide6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
                               QHBoxLayout, QLineEdit, QPushButton, QListWidget,
                               QLabel, QSplitter)
from PySide6.QtWebEngineWidgets import QWebEngineView

# データベースファイル名
DB_FILE = "search_index_ultimate_v5.db"

def init_db():
    """インデックスを保存するデータベースを初期化"""
    conn = sqlite3.connect(DB_FILE)
    cursor = conn.cursor()
    cursor.execute("""
        CREATE TABLE IF NOT EXISTS pages (
            url TEXT PRIMARY KEY,
            domain TEXT,
            title TEXT,
            html TEXT,
            text TEXT
        )
    """)
    conn.commit()
    conn.close()

class SimpleSearchWorker(QThread):
    """【初回のみ稼働】サイト内を巡回してデータベースを構築するクローラー"""
    page_found = Signal(str, str)
    progress_update = Signal(str)
    finished = Signal()

    def __init__(self, start_url, keyword, max_pages=1500):
        super().__init__()
        self.start_url = start_url
        self.keyword = keyword.lower()
        self.max_pages = max_pages
        self.domain = urlparse(start_url).netloc

    def run(self):
        conn = sqlite3.connect(DB_FILE)
        cursor = conn.cursor()

        to_visit = [self.start_url]
        visited = set()
        match_count = 0

        while to_visit and len(visited) < self.max_pages:
            current_url = to_visit.pop(0)
            if current_url in visited:
                continue
            visited.add(current_url)

            parsed_path = urlparse(current_url).path.lower()
            if parsed_path.endswith(('.epub', '.pdf', '.zip', '.rar', '.mp4', '.png', '.jpg', '.jpeg', '.gif')):
                continue

            self.progress_update.emit(f"初回インデックス作成中... (検出:{match_count}件 / 探索済:{len(visited)}頁)")

            try:
                res = requests.get(current_url, timeout=3, headers={"User-Agent": "Mozilla/5.0"})
                if "text/html" not in res.headers.get("Content-Type", ""):
                    continue

                soup = BeautifulSoup(res.content, "html.parser")
                title = soup.title.string.strip() if soup.title else current_url
                text_content = soup.get_text()

                # データベースに丸ごと保存
                cursor.execute(
                    "INSERT OR REPLACE INTO pages (url, domain, title, html, text) VALUES (?, ?, ?, ?, ?)",
                    (current_url, self.domain, title, res.text, text_content)
                )
                conn.commit()

                if self.keyword in text_content.lower():
                    self.page_found.emit(current_url, title)
                    match_count += 1

            except Exception:
                continue

            # リンクの抽出とクレンジング
            for a_tag in soup.find_all("a", href=True):
                next_url = urljoin(current_url, a_tag["href"])
                clean_url = next_url.split('#')[0].split('?')[0] # 安全なクレンジング

                if urlparse(clean_url).netloc == self.domain:
                    if clean_url not in visited and clean_url not in to_visit:
                        to_visit.append(clean_url)

        conn.close()
        self.finished.emit()


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("サイト内一括検索ブラウザ (SQL高速検索＆高精彩表示版)")
        self.resize(1200, 800)

        main_widget = QWidget()
        self.setCentralWidget(main_widget)
        main_layout = QVBoxLayout(main_widget)
        main_layout.setSpacing(10)

        # 上部入力エリア
        top_layout = QHBoxLayout()
        self.url_input = QLineEdit("")
        self.keyword_input = QLineEdit("")
        self.btn_search = QPushButton("サイト内を検索")

        top_layout.addWidget(QLabel("対象URL:"))
        top_layout.addWidget(self.url_input, stretch=3)
        top_layout.addWidget(QLabel("キーワード:"))
        top_layout.addWidget(self.keyword_input, stretch=2)
        top_layout.addWidget(self.btn_search)
        main_layout.addLayout(top_layout)

        self.status_label = QLabel("URLとキーワードを入力して「サイト内を検索」を押してください。")
        main_layout.addWidget(self.status_label)

        # 左右分割画面
        splitter = QSplitter(Qt.Horizontal)

        # 左：結果リスト
        self.result_list = QListWidget()
        self.result_list.itemClicked.connect(self.on_item_clicked)
        splitter.addWidget(self.result_list)

        # 右：プレビュー専用のQWebEngineView（表示崩れが絶対に起きない最高峰のブラウザ）
        self.web_view = QWebEngineView()
        splitter.addWidget(self.web_view)

        splitter.setSizes([300, 900])
        main_layout.addWidget(splitter, stretch=1)

        self.btn_search.clicked.connect(self.start_site_search)
        self.worker = None

    def start_site_search(self):
        url = self.url_input.text().strip()
        keyword = self.keyword_input.text().strip()

        if not url or not keyword:
            self.status_label.setText("URLとキーワードの両方を入力してください。")
            return

        # 【追加】検索開始と同時に、左のリストと右のブラウザ画面をクリアする
        self.result_list.clear()     # 左側の結果一覧を消去
        self.web_view.setHtml("")    # 右側のブラウザ画面を真っ白にする

        # データベースにこのドメインのデータがすでに登録されているかチェック
        domain = urlparse(url).netloc
        conn = sqlite3.connect(DB_FILE)
        cursor = conn.cursor()
        cursor.execute("SELECT COUNT(*) FROM pages WHERE domain = ?", (domain,))
        saved_count = cursor.fetchone()[0]

        if saved_count > 0:
            # 【2回目以降】ループ処理を一切せず、データベースからSQLで一発検索（0秒）
            self.status_label.setText(f"インデックスから一瞬で検索中... (対象: {saved_count} ページ)")

            # SQLiteのLIKE句を使用（大文字小文字を区別しないようlowerに変形）
            cursor.execute(
                "SELECT url, title FROM pages WHERE domain = ? AND lower(text) LIKE ?",
                (domain, f"%{keyword.lower()}%")
            )
            rows = cursor.fetchall()
            conn.close()

            for row_url, row_title in rows:
                self.add_result(row_url, row_title)

            self.status_label.setText(f"検索完了(インデックス高速高速利用)。合計 {len(rows)} 件のページが見つかりました。")
        else:
            # 【初回のみ】まだデータベースが空っぽなら、スレッドを走らせて巡回・構築する
            conn.close()
            self.btn_search.setEnabled(False)
            self.worker = SimpleSearchWorker(url, keyword, max_pages=1500)
            self.worker.page_found.connect(self.add_result)
            self.worker.progress_update.connect(self.status_label.setText)
            self.worker.finished.connect(self.on_search_finished)
            self.worker.start()

    def add_result(self, url, title):
        item_text = f"{title}\n({url})"
        self.result_list.addItem(item_text)
        self.result_list.item(self.result_list.count() - 1).setData(Qt.UserRole, url)

    def on_search_finished(self):
        self.btn_search.setEnabled(True)
        self.status_label.setText(f"検索完了。合計 {self.result_list.count()} 件のページが見つかりました。")

    def on_item_clicked(self, item):
        """リストがクリックされたら、Chromiumブラウザとして完璧な見た目で描画し、自動ハイライト"""
        url = item.data(Qt.UserRole)
        self.web_view.setUrl(QUrl(url))

        # 読み込み完了後に文字を自動ハイライト
        try:
            self.web_view.loadFinished.disconnect()
        except Exception:
            pass

        keyword = self.keyword_input.text()
        self.web_view.loadFinished.connect(lambda: self.web_view.findText(keyword))

if __name__ == "__main__":
    init_db()  # データベースの初期化
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())
