#!/usr/bin/env python3.11
# -*- coding: utf-8 -*-
"""
generate_library_inventory.py

Делает подробную инвентаризацию обеих библиотек в формате Excel (.xlsx),
по два листа на файл:
  - "По алфавиту" - плоский список, отсортированный по названию
  - "Дерево папок" - путь разложен по отдельным колонкам (уровень 1, 2, 3...),
    чтобы в Excel можно было выделить целую подпапку со всеми файлами разом

Колонки: полное название (из метаданных PDF), имя файла, полный путь, язык,
статус распознавания, автор (если есть), тип и размер файла.

Требуется библиотека openpyxl:
    python3.11 -m pip install openpyxl --break-system-packages
"""

import os
import csv

try:
    import fitz  # pymupdf
except ImportError:
    print("Не установлен pymupdf. Установите: python3.11 -m pip install pymupdf --break-system-packages")
    raise SystemExit(1)

try:
    from openpyxl import Workbook
    from openpyxl.styles import Font, Alignment
    from openpyxl.utils import get_column_letter
except ImportError:
    print("Не установлен openpyxl. Установите: python3.11 -m pip install openpyxl --break-system-packages")
    raise SystemExit(1)

OUTPUT_SUFFIX = " (распознанный текст)"  # тот же признак, что используют наши OCR-скрипты

LIBRARIES = [
    {
        "name": "история",
        "root": "/home/dayhanbiz/public_html/3-damja.science/Хранилище библиотеки истории стран бывшего Туркестана и сопредельных стран",
        "csv": "/home/dayhanbiz/public_html/3-damja.science/Обслуживание библиотеки истории стран бывшего Туркестана и сопредельных стран/Скрипты/Регистрация языка документов/language_detection_history.csv",
        "output_dir": "/home/dayhanbiz/public_html/3-damja.science/Менеджер обслуживания библиотеки/Реестр/История",
    },
    {
        "name": "ВАМ",
        "root": "/home/dayhanbiz/public_html/3-damja.science/Хранилище библиотеки водного, сельского и мелиоративного хозяйства и ресурсов стран бывшего Туркестана и сопредельных стран",
        "csv": "/home/dayhanbiz/public_html/3-damja.science/Обслуживание библиотеки водного, сельского и мелиоративного хозяйства и ресурсов стран бывшего Туркестана и сопредельных стран/Скрипты/Регистрация языка документов/language_detection_VAM.csv",
        "output_dir": "/home/dayhanbiz/public_html/3-damja.science/Менеджер обслуживания библиотеки/Реестр/ВАМ",
    },
]

FLAT_HEADERS = ["Полное название", "Имя файла", "Полный путь", "Язык",
                "Статус распознавания", "Автор", "Тип файла", "Размер"]


def human_size(num_bytes):
    for unit in ["Б", "КБ", "МБ", "ГБ"]:
        if num_bytes < 1024:
            return f"{num_bytes:.1f} {unit}"
        num_bytes /= 1024
    return f"{num_bytes:.1f} ТБ"


def get_pdf_metadata(path):
    title, author = "", ""
    try:
        doc = fitz.open(path)
        meta = doc.metadata or {}
        title = (meta.get("title") or "").strip()
        author = (meta.get("author") or "").strip()
        doc.close()
    except Exception:
        pass
    return title, author


def recognition_status(pdf_path, has_text_layer):
    base_name = os.path.splitext(os.path.basename(pdf_path))[0]
    ocr_dir = os.path.join(os.path.dirname(pdf_path), base_name + OUTPUT_SUFFIX)
    if os.path.isdir(ocr_dir):
        return "Распознан (наш OCR)"
    if has_text_layer == "True":
        return "Есть текстовый слой (до OCR)"
    return "Не распознан"


def collect_rows(library):
    rows = []
    if not os.path.exists(library["csv"]):
        print(f"Не найден CSV: {library['csv']}")
        return rows

    with open(library["csv"], encoding="utf-8", newline="") as f:
        reader = csv.DictReader(f, delimiter=";")
        entries = list(reader)

    print(f"[{library['name']}] файлов в реестре: {len(entries)}")

    for i, row in enumerate(entries, 1):
        pdf_path = row.get("absolute_path", "")
        if not pdf_path or not os.path.exists(pdf_path):
            continue

        title, author = get_pdf_metadata(pdf_path)
        filename = os.path.basename(pdf_path)
        display_title = title if title else os.path.splitext(filename)[0]

        try:
            size_bytes = os.path.getsize(pdf_path)
        except OSError:
            size_bytes = 0

        rel_path = os.path.relpath(pdf_path, library["root"])
        path_parts = rel_path.split(os.sep)

        rows.append({
            "title": display_title,
            "filename": filename,
            "full_path": pdf_path,
            "language": row.get("detected_language", ""),
            "status": recognition_status(pdf_path, row.get("has_text_layer", "")),
            "author": author,
            "file_type": os.path.splitext(filename)[1].lstrip(".").upper() or "PDF",
            "size": human_size(size_bytes),
            "path_parts": path_parts,  # включая имя файла последним элементом
        })

        if i % 1000 == 0:
            print(f"  обработано {i}/{len(entries)}", flush=True)

    return rows


def write_flat_sheet(ws, rows):
    ws.title = "По алфавиту"
    ws.append(FLAT_HEADERS)
    for cell in ws[1]:
        cell.font = Font(bold=True)
    ws.freeze_panes = "A2"

    for r in sorted(rows, key=lambda x: x["title"].lower()):
        ws.append([r["title"], r["filename"], r["full_path"], r["language"],
                   r["status"], r["author"], r["file_type"], r["size"]])

    widths = [45, 30, 60, 22, 26, 20, 10, 10]
    for idx, w in enumerate(widths, 1):
        ws.column_dimensions[get_column_letter(idx)].width = w


def write_tree_sheet(ws, rows):
    ws.title = "Дерево папок"
    max_depth = max((len(r["path_parts"]) for r in rows), default=1)
    level_headers = [f"Уровень {i+1}" for i in range(max_depth)]
    headers = level_headers + FLAT_HEADERS[3:]  # без "Полное название"/"Имя файла"/"Полный путь" - они в уровнях
    # Оставим и полное название/автора/статус и т.д. после уровней папок
    headers = level_headers + ["Полное название", "Язык", "Статус распознавания", "Автор", "Тип файла", "Размер", "Полный путь"]
    ws.append(headers)
    for cell in ws[1]:
        cell.font = Font(bold=True)
    ws.freeze_panes = get_column_letter(max_depth + 1) + "2"

    def sort_key(r):
        return [p.lower() for p in r["path_parts"]]

    for r in sorted(rows, key=sort_key):
        level_cells = list(r["path_parts"]) + [""] * (max_depth - len(r["path_parts"]))
        ws.append(level_cells + [r["title"], r["language"], r["status"],
                                  r["author"], r["file_type"], r["size"], r["full_path"]])

    for idx in range(1, max_depth + 1):
        ws.column_dimensions[get_column_letter(idx)].width = 28
    extra_widths = [45, 22, 26, 20, 10, 10, 60]
    for offset, w in enumerate(extra_widths):
        ws.column_dimensions[get_column_letter(max_depth + 1 + offset)].width = w


def main():
    for library in LIBRARIES:
        os.makedirs(library["output_dir"], exist_ok=True)
        rows = collect_rows(library)
        if not rows:
            print(f"[{library['name']}] нет данных, пропускаю")
            continue

        wb = Workbook()
        write_flat_sheet(wb.active, rows)
        write_tree_sheet(wb.create_sheet(), rows)

        output_path = os.path.join(library["output_dir"], f"Инвентаризация_{library['name']}.xlsx")
        wb.save(output_path)
        print(f"[{library['name']}] сохранено: {output_path} (строк: {len(rows)})")


if __name__ == "__main__":
    main()
