"""
Модуль для конвертации HTML в PDF с использованием системного WeasyPrint (Homebrew)
Фильтрация пустых строк таблиц, центрирование по text-align: center.
"""

import os
import re
import subprocess
import tempfile
from typing import List, Dict, Tuple
from pathlib import Path
from utils import logger


class WeasyPrintConverter:
    def __init__(self, output_dirs: list[str]):
        self.output_dirs = [Path(d).resolve() for d in output_dirs]
        self.max_files = 5
        for output_dir in self.output_dirs:
            output_dir.mkdir(parents=True, exist_ok=True)

        self.weasyprint_path = self._find_weasyprint()

    def _find_weasyprint(self):
        try:
            result = subprocess.run(
                ["which", "weasyprint"], capture_output=True, text=True, check=True
            )
            weasyprint_path = result.stdout.strip()
            logger.info(f"✅ WeasyPrint найден: {weasyprint_path}")
            return weasyprint_path
        except subprocess.CalledProcessError:
            possible_paths = [
                "/opt/homebrew/bin/weasyprint",
                "/usr/local/bin/weasyprint",
                "/usr/bin/weasyprint",
            ]

            for path in possible_paths:
                if os.path.exists(path):
                    logger.info(f"✅ WeasyPrint найден: {path}")
                    return path

            logger.error("❌ WeasyPrint не найден. Установите: brew install weasyprint")
            return None

    def _clean_filename(self, filename: str) -> str:
        """Очищает имя файла от недопустимых символов"""
        # Только заменяем запрещённые символы на подчёркивание
        clean_name = re.sub(r'[<>:"/\\|?*]', "_", filename)

        # Убираем пробелы по краям
        clean_name = clean_name.strip()

        # Ограничиваем длину
        if len(clean_name) > 200:
            clean_name = clean_name[:200]

        # Защита от пустого имени
        return clean_name if clean_name else "document"

    def _remove_empty_table_rows_comprehensive(self, html_content: str) -> str:
        """
        Комплексное удаление пустых строк таблиц ВНУТРИ таблиц
        Убирает строки без текстового содержимого внутри <table>...</table>
        """
        try:
            # Находим все таблицы
            tables = list(
                re.finditer(
                    r"<table[^>]*>.*?</table>",
                    html_content,
                    flags=re.IGNORECASE | re.DOTALL,
                )
            )

            if not tables:
                return html_content

            # Обрабатываем с конца, чтобы не сбивать индексы
            for table_match in reversed(tables):
                table_html = table_match.group(0)
                table_start = table_match.start()
                table_end = table_match.end()

                # Разбираем таблицу на строки
                rows = []
                pos = 0
                table_length = len(table_html)

                while pos < table_length:
                    # Ищем начало строки
                    tr_start = table_html.lower().find("<tr", pos)
                    if tr_start == -1:
                        break

                    # Ищем конец строки
                    tr_end = table_html.lower().find("</tr>", tr_start)
                    if tr_end == -1:
                        # Нет закрывающего тега - ищем конец таблицы
                        tr_end = table_html.lower().find("</table>", tr_start)
                        if tr_end == -1:
                            tr_end = table_length

                    tr_end += 5  # Длина '</tr>'
                    row_html = table_html[tr_start:tr_end]

                    # Проверяем строку на пустоту
                    is_empty_row = self._is_empty_table_row(row_html)

                    if not is_empty_row:
                        # Сохраняем непустую строку
                        rows.append(row_html)
                    else:
                        logger.debug(f"Удалена пустая строка таблицы")

                    pos = tr_end

                if len(rows) == 0:
                    # Если все строки пустые, удаляем всю таблицу
                    logger.info(f"🗑️  Удалена полностью пустая таблица")
                    html_content = html_content[:table_start] + html_content[table_end:]
                else:
                    # Собираем таблицу обратно
                    new_table_html = table_html
                    for row in rows:
                        # Убедимся, что строка уже в таблице
                        if row not in new_table_html:
                            # Это не должно происходить, но на всякий случай
                            pass

                    # Заменяем оригинальную таблицу (она осталась неизменной если строки не удалялись)
                    # Но мы все равно пересобираем для чистоты
                    table_header_end = (
                        table_html.lower().find(">", table_html.lower().find("<table"))
                        + 1
                    )
                    table_footer_start = table_html.lower().rfind("</table>")

                    new_table = (
                        table_html[:table_header_end]
                        + "".join(rows)
                        + table_html[table_footer_start:]
                    )
                    html_content = (
                        html_content[:table_start]
                        + new_table
                        + html_content[table_end:]
                    )

            return html_content

        except Exception as e:
            logger.warning(f"⚠ Ошибка удаления пустых строк таблиц: {e}")
            return html_content

    def _is_empty_table_row(self, row_html: str) -> bool:
        """
        Проверяет, является ли строка таблицы пустой
        """
        # Убираем все HTML теги
        text_only = re.sub(r"<[^>]+>", "", row_html)

        # Убираем все пробелы, невидимые символы и спецсимволы
        clean_text = re.sub(r"[\s\xa0\u200b\u200c\u200d\u2060\ufeff]+", "", text_only)

        # Убираем HTML entities (но оставляем &nbsp; как признак непустоты)
        clean_text = re.sub(r"&[^;]{2,6};", "", clean_text)

        # Проверяем специальные случаи
        has_colspan = "colspan" in row_html.lower()
        has_rowspan = "rowspan" in row_html.lower()
        has_nbsp = "&nbsp;" in row_html or "&#160;" in row_html

        # Строка считается НЕ пустой если:
        # 1. Есть текст после очистки
        # 2. Есть объединенные ячейки
        # 3. Есть неразрывные пробелы
        is_not_empty = bool(clean_text) or has_colspan or has_rowspan or has_nbsp

        return not is_not_empty

    def _center_elements_with_center_alignment(self, html_content: str) -> str:
        """
        Находит элементы с text-align: center и гарантирует их центрирование
        """
        try:
            # Ищем элементы со стилем text-align: center
            def add_center_alignment(match):
                full_match = match.group(0)
                element_with_style = match.group(1)
                element_name = match.group(2)
                style_content = match.group(3)

                # Если уже есть align="center", оставляем как есть
                if (
                    'align="center"' in element_with_style
                    or "align='center'" in element_with_style
                ):
                    return full_match

                # Добавляем align="center" к элементу
                if element_name in [
                    "p",
                    "div",
                    "h1",
                    "h2",
                    "h3",
                    "h4",
                    "h5",
                    "h6",
                    "td",
                    "th",
                ]:
                    new_element = element_with_style.replace(
                        f"<{element_name}", f'<{element_name} align="center"'
                    )
                    return new_element + match.group(4)

                return full_match

            # Паттерн для поиска элементов с text-align: center
            pattern = r'(<(\w+)[^>]*style\s*=\s*["\'][^"\']*text-align\s*:\s*center[^"\']*["\'][^>]*>)(.*?)'

            html_content = re.sub(
                pattern,
                add_center_alignment,
                html_content,
                flags=re.IGNORECASE | re.DOTALL,
            )

            return html_content

        except Exception as e:
            # logger.warning(f"⚠ Ошибка центрирования элементов: {e}")
            return html_content

    def _prepare_html(self, html_content: str) -> str:
        """Подготавливает HTML для конвертации"""
        try:
            # 1. Удаляем изображения
            html_content = re.sub(r"<img[^>]*>", "", html_content, flags=re.IGNORECASE)

            # 2. Убираем ВСЕ пустые строки таблиц (основная задача)
            html_content = self._remove_empty_table_rows_comprehensive(html_content)

            # 3. Центрируем элементы с text-align: center
            html_content = self._center_elements_with_center_alignment(html_content)

            # 4. Добавляем базовую структуру если её нет
            if not html_content.strip().startswith("<!DOCTYPE"):
                html_content = f"""<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <style>
        /* МИНИМАЛЬНЫЕ СТИЛИ - почти не вмешиваемся в оригинальный формат */
        
        @page {{ 
            size: A4; 
            margin: 0.5cm !important;
        }}
        
        /* Основные настройки */
        body {{ 
            font-family: "DejaVu Sans", Arial, sans-serif;
            font-size: 9pt !important;
            line-height: 1.0 !important;
            color: #000000;
        }}
        
        /* ТАБЛИЦЫ - сохраняем оригинальный формат! */
        table {{
            border-collapse: collapse !important;
            margin: 0 !important; /* Убрали все отступы вокруг таблиц */
            font-size: 8.5pt !important;
            page-break-inside: auto !important;
        }}
        
        /* ЯЧЕЙКИ ТАБЛИЦ - только вертикальное центрирование и небольшие отступы */
        th, td {{
            padding: 3px 5px !important; /* Минимальные отступы */
            vertical-align: middle !important; /* Вертикальное центрирование */
        }}
        
        /* НЕ добавляем границы автоматически! */
        /* Границы будут ТОЛЬКО там, где они были в оригинале */
        
        /* Горизонтальное центрирование заголовков таблиц */
        th {{
            text-align: center !important;
        }}
        
        /* Элементы с align="center" */
        [align="center"] {{
            text-align: center !important;
        }}
        
        /* Элементы со style*="text-align: center" */
        [style*="text-align: center"] {{
            text-align: center !important;
        }}
        
        /* Заголовки документа */
        h1, h2, h3, h4, h5, h6 {{
            font-weight: bold !important;
            margin-top: 8px !important;
            margin-bottom: 4px !important;
        }}
        
        h1 {{ font-size: 11pt !important; page-break-before: always !important; }}
        h2 {{ font-size: 10pt !important; }}
        h3 {{ font-size: 9.5pt !important; }}
        
        /* Параграфы - минимальные отступы */
        p {{
            margin: 0 0 3px 0 !important;
        }}
        
        /* Форматирование текста */
        strong, b {{
            font-weight: bold !important;
        }}
        
        em, i {{
            font-style: italic !important;
        }}
        
        /* Списки */
        ul, ol {{
            margin: 3px 0 3px 15px !important;
            padding: 0 !important;
        }}
        
        li {{
            margin-bottom: 1px !important;
        }}
        
        /* Убираем лишние отступы */
        div, p, span {{
            margin: 0 !important;
            padding: 0 !important;
        }}
        
        br {{
            line-height: 0.5 !important;
            margin: 0 !important;
            padding: 0 !important;
        }}
        
        /* Убираем отступы перед таблицами на уровне CSS */
        :before(table) {{
            margin-bottom: 0 !important;
            padding-bottom: 0 !important;
        }}
    </style>
</head>
<body>
{html_content}
</body>
</html>"""

            # 5. Убеждаемся в наличии charset
            if "<meta charset=" not in html_content.lower():
                html_content = html_content.replace(
                    "<head>", '<head><meta charset="UTF-8">', 1
                )

            # 6. ОБРАБОТКА ТАБЛИЦ:
            # Только добавляем вертикальное центрирование и отступы, НЕ меняем границы!

            # Добавляем vertical-align: middle ко всем ячейкам если его нет
            def add_vertical_align(match):
                cell_tag = match.group(0)
                if (
                    "valign=" not in cell_tag.lower()
                    and "vertical-align:" not in cell_tag.lower()
                ):
                    return cell_tag.replace("<td", '<td valign="middle"').replace(
                        "<th", '<th valign="middle"'
                    )
                return cell_tag

            html_content = re.sub(
                r"<(td|th)(?![^>]*(?:valign|vertical-align))[^>]*>",
                add_vertical_align,
                html_content,
                flags=re.IGNORECASE,
            )

            # Добавляем text-align: center к заголовкам таблиц если нет своего выравнивания
            def add_th_center(match):
                th_tag = match.group(0)
                if (
                    "align=" not in th_tag.lower()
                    and "text-align:" not in th_tag.lower()
                ):
                    return th_tag.replace("<th", '<th align="center"')
                return th_tag

            html_content = re.sub(
                r"<th[^>]*>", add_th_center, html_content, flags=re.IGNORECASE
            )

            # 7. ФИНАЛЬНАЯ ПРОВЕРКА: еще раз удаляем пустые строки таблиц после всех модификаций
            html_content = self._remove_empty_table_rows_comprehensive(html_content)

            logger.debug(f"HTML подготовлен, размер: {len(html_content)} символов")

            return html_content

        except Exception as e:
            logger.error(f"❌ Ошибка подготовки HTML: {e}")
            # Минимальная обработка в случае ошибки
            return f"""<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <style>
        @page {{ margin: 0.5cm; }}
        body {{ font-size: 9pt; line-height: 1.0; }}
        table {{ border-collapse: collapse; margin: 0; }}
        th, td {{ padding: 3px 5px; vertical-align: middle; }}
        th {{ text-align: center; }}
    </style>
</head>
<body>{html_content}</body>
</html>"""

    def convert_html_to_pdf(
        self, html_content: str, output_paths: list[Path]
    ) -> Tuple[bool, str]:
        """
        Конвертирует HTML в PDF и сохраняет во все указанные папки
        Оптимизированная версия: конвертирует один раз, копирует результат
        """
        if not self.weasyprint_path:
            return False, "WeasyPrint не найден. Установите: brew install weasyprint"

        try:
            prepared_html = self._prepare_html(html_content)

            with tempfile.NamedTemporaryFile(
                mode="w", suffix=".html", delete=False, encoding="utf-8"
            ) as f:
                f.write(prepared_html)
                temp_html = f.name

            try:
                # Создаем временный PDF
                with tempfile.NamedTemporaryFile(
                    suffix=".pdf", delete=False
                ) as temp_pdf_file:
                    temp_pdf = temp_pdf_file.name

                # Конвертируем во временный файл
                cmd = [
                    self.weasyprint_path,
                    temp_html,
                    temp_pdf,
                    "--encoding",
                    "utf-8",
                    "--presentational-hints",
                ]

                logger.debug("Выполняем однократную конвертацию WeasyPrint")

                result = subprocess.run(
                    cmd, capture_output=True, text=True, timeout=180
                )

                if result.returncode != 0:
                    error_msg = (
                        result.stderr[:300] if result.stderr else "Неизвестная ошибка"
                    )
                    return False, f"Ошибка конвертации: {error_msg}"

                # Проверяем временный PDF
                temp_pdf_path = Path(temp_pdf)
                if not temp_pdf_path.exists() or temp_pdf_path.stat().st_size < 1024:
                    return False, "Созданный PDF слишком мал или отсутствует"

                # Копируем во все целевые папки
                successful_files = []
                failed_files = []

                for output_path in output_paths:
                    try:
                        # Создаем родительскую папку
                        output_path.parent.mkdir(parents=True, exist_ok=True)

                        # Копируем файл
                        import shutil

                        shutil.copy2(temp_pdf, output_path)

                        # Проверяем результат
                        if output_path.exists() and output_path.stat().st_size > 1024:
                            successful_files.append(str(output_path))
                            logger.debug(f"✅ PDF сохранен: {output_path}")
                        else:
                            failed_files.append(
                                f"{output_path}: файл поврежден или слишком мал"
                            )

                    except Exception as e:
                        failed_files.append(f"{output_path}: {str(e)}")
                        logger.error(f"❌ Ошибка копирования в {output_path}: {str(e)}")

                # Удаляем временный PDF
                os.unlink(temp_pdf)

                # Формируем результат
                if successful_files and not failed_files:
                    return (
                        True,
                        f"✅ PDF создан и сохранен во все {len(successful_files)} папок",
                    )
                elif successful_files:
                    return (
                        True,
                        f"⚠️ PDF создан, но сохранен только в {len(successful_files)}/{len(output_paths)} папок",
                    )
                else:
                    return False, f"❌ Не удалось сохранить PDF ни в одну папку"

            finally:
                if os.path.exists(temp_html):
                    os.unlink(temp_html)

        except subprocess.TimeoutExpired:
            return False, "Таймаут при конвертации (180 секунд)"
        except Exception as e:
            logger.error(f"❌ Неожиданная ошибка: {str(e)}")
            return False, f"Ошибка конвертации: {str(e)}"

    def convert_documents(self, docs: List[Dict]) -> Dict[str, Dict]:
        results = {
            "status": "SUCCESS",
            "files": {},
        }

        if len(docs) > self.max_files:
            logger.warning(
                f"⚠ Получено {len(docs)} документов, обработано {self.max_files}"
            )
            docs = docs[: self.max_files]

        for i, doc in enumerate(docs, 1):
            doc_number = str(doc.get("number", f"doc_{i}")).replace("/", "_")
            doc_title = str(doc.get("title", f"Документ_{i}"))
            html_content = doc.get("data", "")

            safe_title = self._clean_filename(doc_title)
            filename = f"{doc_number}_{safe_title}.pdf"
            output_paths = [output_dir / filename for output_dir in self.output_dirs]

            for path in self.output_dirs:
                key = path.parts[-1]
                if key not in results["files"]:
                    results["files"][key] = []
                results["files"][key].append(
                    {
                        "number": int(doc_number),
                        "title": doc_title,
                        "storagePath": str(path / filename),
                    }
                )

            logger.info(f"📄 Документ {i}/{len(docs)}: {filename}")

            if not html_content or len(html_content.strip()) < 100:
                results["status"] = "ERROR"
                logger.warning(f"   ⚠ Пропущен: недостаточно контента")
                continue

            success, message = self.convert_html_to_pdf(html_content, output_paths)

            if success:
                logger.info(f"   ✅ Успешно")
            else:
                logger.error(f"   ❌ Ошибка: {message}")

        return results


def convert_docs_to_pdfs(docs: List[Dict], patientsData: list[dict]) -> Dict:
    try:
        baseDir = "src/attachments/export/"
        outputDirs = [f"{baseDir}{patient['idPatientMis']}" for patient in patientsData]
        converter = WeasyPrintConverter(outputDirs)
        return converter.convert_documents(docs)

    except Exception as e:
        logger.error(f"❌ Критическая ошибка в convert_docs_to_pdfs: {str(e)}")

        return {
            "status": "ERROR",
            "message": f"Критическая ошибка в convert_docs_to_pdfs: {str(e)}",
        }
