import os
import sqlite3
import urllib.parse
from pathlib import Path
from waitress import serve
import json
import logging
import traceback
import mimetypes

LOG_FILE = Path("/home/dayhanbiz/public_html/Библиотека 3-DAMJA/web/app_debug.log")
logging.basicConfig(
    filename=str(LOG_FILE),
    level=logging.INFO,
    format='%(asctime)s [%(levelname)s] %(message)s',
    encoding='utf-8'
)

BASE_DIR = Path("/home/dayhanbiz/public_html/Библиотека 3-DAMJA")
DB_PATH = BASE_DIR / "search_databases" / "library_search.db"
TEMPLATES_DIR = BASE_DIR / "web" / "templates"
CATALOG_DIR = BASE_DIR / "catalog"
STORAGE_ROOT = Path("/home/dayhanbiz/public_html/3-damja.science/storage")

def get_db_connection():
    conn = sqlite3.connect(DB_PATH)
    conn.row_factory = sqlite3.Row
    return conn

def load_hash_to_path_map():
    mapping = {}
    for json_name in ["list_history.json", "list_vam.json"]:
        json_path = CATALOG_DIR / json_name
        if json_path.exists():
            try:
                with open(json_path, "r", encoding="utf-8") as f:
                    items = json.load(f)
                    for item in items:
                        if "hash" in item and "path" in item:
                            full_path = Path(item["path"])
                            mapping[item["hash"]] = {
                                "file_path": full_path,
                                "name": item["name"]
                            }
            except Exception as e:
                logging.error(f"Ошибка загрузки каталога {json_name}: {e}")
    return mapping

HASH_MAP = load_hash_to_path_map()

def application(environ, start_response):
    path = urllib.parse.unquote(environ.get('PATH_INFO', ''))
    query_string = environ.get('QUERY_STRING', '')
    query_params = urllib.parse.parse_qs(query_string)

    try:
        if path == '/' or path == '':
            return handle_index(query_params, start_response)
        elif path.startswith('/api/page/'):
            return handle_api_page(path, start_response)
        elif path.startswith('/storage/'):
            return handle_storage_file(path, environ, start_response)
        else:
            start_response('404 Not Found', [('Content-Type', 'text/plain; charset=utf-8')])
            return [b"Not Found"]
    except Exception as e:
        logging.critical(f"Критическая ошибка: {e}\n{traceback.format_exc()}")
        start_response('500 Internal Server Error', [('Content-Type', 'text/plain; charset=utf-8')])
        return [b"Internal Server Error"]

def handle_storage_file(path, environ, start_response):
    # Ожидаем путь вида /storage/HASH.pdf
    filename = path[len('/storage/'):]
    target_file_path = None

    if filename.endswith('.pdf'):
        possible_hash = filename[:-4]
        if possible_hash in HASH_MAP:
            target_file_path = HASH_MAP[possible_hash]["file_path"]

    if target_file_path and target_file_path.exists() and target_file_path.is_file():
        file_size = target_file_path.stat().st_size
        mime_type, _ = mimetypes.guess_type(str(target_file_path))
        mime_type = mime_type or 'application/octet-stream'

        headers = [
            ('Content-Type', mime_type),
            ('Content-Length', str(file_size)),
            ('Accept-Ranges', 'bytes')
        ]

        range_header = environ.get('HTTP_RANGE')
        if range_header:
            try:
                bytes_range = range_header.replace('bytes=', '')
                start_str, end_str = bytes_range.split('-')
                start = int(start_str)
                end = int(end_str) if end_str else file_size - 1
                length = end - start + 1

                headers.extend([
                    ('Content-Range', f'bytes {start}-{end}/{file_size}'),
                    ('Content-Length', str(length))
                ])
                start_response('206 Partial Content', headers)

                with open(target_file_path, 'rb') as f:
                    f.seek(start)
                    remaining = length
                    while remaining > 0:
                        chunk_size = min(65536, remaining)
                        chunk = f.read(chunk_size)
                        if not chunk:
                            break
                        remaining -= len(chunk)
                        yield chunk
                return
            except Exception:
                pass

        start_response('200 OK', headers)
        with open(target_file_path, 'rb') as f:
            while True:
                chunk = f.read(65536)
                if not chunk:
                    break
                yield chunk
        return
    else:
        logging.warning(f"Файл не найден по хэшу: {path} (ожидался хэш из filename: {filename})")
        start_response('404 Not Found', [('Content-Type', 'text/plain; charset=utf-8')])
        return [b"File Not Found"]

def handle_index(query_params, start_response):
    query = query_params.get('q', [''])[0].strip()
    page = int(query_params.get('page', [1])[0])
    per_page = 10
    offset = (page - 1) * per_page

    results = []
    total_results = 0

    if query:
        try:
            conn = get_db_connection()
            cursor = conn.cursor()
            cursor.execute("SELECT COUNT(*) as cnt FROM pages_fts WHERE pages_fts MATCH ?", (query,))
            total_results = cursor.fetchone()['cnt']

            search_sql = """
                SELECT book_hash, page_number, snippet(pages_fts, 2, '<mark class="bg-amber-200 text-amber-900 px-1 rounded">', '</mark>', '...', 48) as snippet
                FROM pages_fts
                WHERE pages_fts MATCH ?
                ORDER BY rank
                LIMIT ? OFFSET ?
            """
            cursor.execute(search_sql, (query, per_page, offset))
            results = [dict(row) for row in cursor.fetchall()]
            conn.close()
        except Exception as e:
            logging.error(f"Ошибка поиска: {e}")

    template_path = TEMPLATES_DIR / "index.html"
    if template_path.exists():
        with open(template_path, "r", encoding="utf-8") as f:
            html_template = f.read()

        rendered = html_template.replace('{{ query }}', query)
        rendered = rendered.replace('{{ total }}', str(total_results))

        results_html = ""
        if query:
            if results:
                for item in results:
                    b_hash = item['book_hash']
                    short_hash = b_hash[:16]
                    snippet = item['snippet']
                    p_num = item['page_number']

                    file_info = HASH_MAP.get(b_hash, {})
                    file_name = file_info.get("name", f"Документ {short_hash}...")

                    # Формируем надежную ссылку через хэш
                    rel_url_path = f"/storage/{b_hash}.pdf"
                    web_url = f"{rel_url_path}#page={p_num}"

                    results_html += f"""
                    <div class="bg-white border border-slate-200/85 rounded-2xl p-6 shadow-sm hover:shadow-md transition-shadow mb-4">
                        <div class="flex items-center justify-between text-xs font-medium text-slate-400 mb-2">
                            <span class="flex items-center space-x-1.5">
                                <span class="text-slate-600 font-semibold">Название:</span> <span class="font-medium text-slate-700">{file_name}</span>
                            </span>
                            <span class="bg-emerald-50 text-emerald-800 px-2.5 py-1 rounded-lg text-xs font-semibold">
                                Страница {p_num}
                            </span>
                        </div>
                        <p class="text-slate-700 text-sm leading-relaxed mb-4 bg-slate-50/70 p-3.5 rounded-xl border border-slate-100">
                            {snippet}
                        </p>
                        <div class="flex items-center justify-between">
                            <a href="{web_url}" target="_blank" class="text-xs font-semibold text-slate-700 hover:text-slate-900 bg-emerald-50 hover:bg-emerald-100 px-3.5 py-2 rounded-xl transition-colors inline-flex items-center space-x-1.5">
                                <span>📄 Открыть оригинал на странице с найденным словом</span>
                            </a>
                            <button onclick="openPageModal('{b_hash}', {p_num})" class="inline-flex items-center space-x-1.5 text-xs font-semibold text-slate-600 hover:text-slate-800 bg-slate-100 hover:bg-slate-200 px-3.5 py-2 rounded-xl transition-colors cursor-pointer">
                                <span>📖 Открыть распознанный текст страницы</span>
                            </button>
                        </div>
                    </div>
                    """
                results_block = f'<div class="space-y-4"><h2 class="text-xl font-semibold text-slate-900 mb-4">Результаты поиска</h2>{results_html}</div>'
            else:
                results_block = """
                <div class="bg-white border border-slate-200 rounded-2xl p-12 text-center shadow-sm">
                    <p class="text-slate-500 text-base mb-2">По вашему запросу ничего не найдено.</p>
                    <p class="text-xs text-slate-400">Попробуйте изменить формулировку или использовать другие ключевые слова.</p>
                </div>
                """
        else:
            results_block = """
            <div class="py-20 text-center max-w-lg mx-auto">
                <div class="w-16 h-16 bg-emerald-100 text-emerald-700 rounded-2xl mx-auto flex items-center justify-center text-2xl mb-4 font-bold shadow-sm">🔍</div>
                <h2 class="text-2xl font-bold text-slate-900 mb-2">Добро пожаловать в архив</h2>
                <p class="text-slate-500 text-sm mb-6">Введите поисковый запрос выше, чтобы мгновенно найти нужные факты, термины и исторические документы по всей базе знаний.</p>
            </div>
            """

        if '<main class="max-w-5xl mx-auto px-4 py-8 flex-1 w-full">' in rendered:
            parts = rendered.split('<main class="max-w-5xl mx-auto px-4 py-8 flex-1 w-full">')
            main_end_parts = parts[1].split('</main>')
            rendered = parts[0] + '<main class="max-w-5xl mx-auto px-4 py-8 flex-1 w-full">\n' + results_block + '\n</main>' + main_end_parts[1]

        start_response('200 OK', [('Content-Type', 'text/html; charset=utf-8')])
        return [rendered.encode('utf-8')]
    else:
        start_response('404 Not Found', [('Content-Type', 'text/plain; charset=utf-8')])
        return [b"Template not found"]

def handle_api_page(path, start_response):
    parts = path.split('/')
    if len(parts) >= 5:
        book_hash = parts[3]
        try:
            page_num = int(parts[4])
            page_file = BASE_DIR / "extracted_texts" / book_hash / f"page_{page_num:04d}.txt"
            if page_file.exists():
                with open(page_file, "r", encoding="utf-8", errors="ignore") as f:
                    content = f.read()
                response_data = {"success": True, "content": content, "book_hash": book_hash, "page": page_num}
            else:
                response_data = {"success": False, "error": "Страница не найдена"}
        except ValueError:
            response_data = {"success": False, "error": "Неверный номер страницы"}

        body = json.dumps(response_data, ensure_ascii=False).encode('utf-8')
        start_response('200 OK', [('Content-Type', 'application/json; charset=utf-8')])
        return [body]
    else:
        start_response('400 Bad Request', [('Content-Type', 'text/plain; charset=utf-8')])
        return [b"Bad Request"]

if __name__ == '__main__':
    serve(application, host='127.0.0.1', port=5000, threads=8)
