import json
import os
from pathlib import Path
from reportlab.lib.pagesizes import letter
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, PageBreak
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib import colors
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from src.core.database.managers.scanner_manager import ScannerManager
from src.utils.logger import get_logger

logger = get_logger("reports")

# Определяем базовый путь проекта
BASE_DIR = Path(__file__).resolve().parent.parent.parent.parent

# Регистрируем шрифт Exo 2
def register_exo2_font():
    """Регистрация шрифта Exo 2 для поддержки кириллицы"""
    try:
        font_path = BASE_DIR / "fonts" / "Exo2.ttf"
        
        # Регистрируем обычное начертание
        pdfmetrics.registerFont(TTFont('Exo2', str(font_path)))
        
        # Регистрируем жирное начертание (используем тот же файл, но с другим именем)
        pdfmetrics.registerFont(TTFont('Exo2-Bold', str(font_path)))
        
        # Создаем псевдо-курсивное начертание (если нужно)
        pdfmetrics.registerFont(TTFont('Exo2-Italic', str(font_path)))
        
        # Регистрируем семейство шрифтов
        pdfmetrics.registerFontFamily('Exo2', normal='Exo2', bold='Exo2-Bold', italic='Exo2-Italic')
        return True
    except Exception as e:
        logger.error(f"Ошибка регистрации шрифта Exo 2: {e}")
        # Возвращаем False, если шрифт не удалось зарегистрировать
        return False

# Функция generate_json_report остается без изменений

def generate_pdf_report(scanner_service: ScannerManager, session_id: int, output_path: str):
    """Генерация PDF отчета для указанной сессии с использованием шрифта Exo 2"""
    try:
        # Регистрируем шрифт
        font_registered = register_exo2_font()
        base_font = 'Exo2' if font_registered else 'Helvetica'
        
        session = scanner_service.get_session_by_id(session_id)
        if not session:
            logger.warning(f"Сессия с ID {session_id} не найдена")
            return

        scans = scanner_service.get_scans_by_session(session_id)
        attacks = scanner_service.get_attacks_by_session(session_id)

        # Создаем PDF документ
        doc = SimpleDocTemplate(output_path, pagesize=letter)
        
        # Создаем кастомные стили с использованием Exo 2
        styles = getSampleStyleSheet()
        
        # Переопределяем стили для поддержки Exo 2
        title_style = ParagraphStyle(
            'Title',
            parent=styles['Title'],
            fontName=f'{base_font}-Bold',
            fontSize=18,
            alignment=1,
            spaceAfter=12
        )
        
        heading1_style = ParagraphStyle(
            'Heading1',
            parent=styles['Heading1'],
            fontName=f'{base_font}-Bold',
            fontSize=14,
            spaceAfter=6
        )
        
        heading2_style = ParagraphStyle(
            'Heading2',
            parent=styles['Heading2'],
            fontName=f'{base_font}-Bold',
            fontSize=12,
            spaceAfter=6
        )
        
        heading3_style = ParagraphStyle(
            'Heading3',
            parent=styles['Heading3'],
            fontName=f'{base_font}-Bold',
            fontSize=11,
            spaceAfter=6
        )
        
        heading4_style = ParagraphStyle(
            'Heading4',
            parent=styles['Heading4'],
            fontName=base_font,
            fontSize=10,
            spaceAfter=4
        )
        
        normal_style = ParagraphStyle(
            'Normal',
            parent=styles['Normal'],
            fontName=base_font,
            fontSize=10,
            spaceAfter=6
        )
        
        table_header_style = ParagraphStyle(
            'TableHeader',
            parent=styles['Normal'],
            fontName=f'{base_font}-Bold',
            fontSize=10,
            alignment=1,
            textColor=colors.black
        )
        
        table_cell_style = ParagraphStyle(
            'TableCell',
            parent=styles['Normal'],
            fontName=base_font,
            fontSize=9,
            alignment=1
        )

        elements = []

        # Заголовок отчета
        elements.append(Paragraph(f"Отчет по тестированию безопасности", title_style))
        elements.append(Paragraph(f"Сессия: {session.name}", heading2_style))
        elements.append(Spacer(1, 12))

        # Информация о сессии
        elements.append(Paragraph("Информация о сессии", heading2_style))
        session_data = [
            [Paragraph("ID", table_header_style), Paragraph(str(session.id), table_cell_style)],
            [Paragraph("Название", table_header_style), Paragraph(session.name, table_cell_style)],
            [Paragraph("Дата создания", table_header_style), Paragraph(str(session.created_at), table_cell_style)]
        ]
        session_table = Table(session_data, colWidths=[100, 400])
        session_table.setStyle(TableStyle([
            ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor("#4A6F9E")),
            ('BACKGROUND', (0, 1), (-1, -1), colors.HexColor("#E8EDF4")),
            ('GRID', (0, 0), (-1, -1), 1, colors.black),
            ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
        ]))
        elements.append(session_table)
        elements.append(Spacer(1, 24))

        # Сканирования
        if scans:
            elements.append(PageBreak())
            elements.append(Paragraph("Результаты сканирования", heading1_style))
            
            for scan in scans:
                elements.append(Paragraph(f"Сканирование: {scan.tool}", heading2_style))
                scan_data = [
                    [Paragraph("Параметр", table_header_style), Paragraph("Значение", table_header_style)],
                    [Paragraph("ID", table_header_style), Paragraph(str(scan.id), table_cell_style)],
                    [Paragraph("Аргументы", table_header_style), Paragraph(scan.args or "Нет", table_cell_style)],
                    [Paragraph("Сводка", table_header_style), Paragraph(scan.summary or "Нет", table_cell_style)],
                    [Paragraph("Длительность", table_header_style), Paragraph(f"{scan.duration} секунд", table_cell_style)],
                    [Paragraph("Дата создания", table_header_style), Paragraph(str(scan.created_at), table_cell_style)]
                ]
                scan_table = Table(scan_data, colWidths=[150, 350])
                scan_table.setStyle(TableStyle([
                    ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor("#4A6F9E")),
                    ('BACKGROUND', (0, 1), (-1, -1), colors.HexColor("#E8EDF4")),
                    ('GRID', (0, 0), (-1, -1), 1, colors.black),
                    ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
                ]))
                elements.append(scan_table)
                elements.append(Spacer(1, 12))

                # Хосты
                hosts = scanner_service.get_hosts_by_scan(scan.id)
                if hosts:
                    elements.append(Paragraph("Обнаруженные хосты", heading3_style))
                    host_data = [[
                        Paragraph("IP адрес", table_header_style),
                        Paragraph("MAC адрес", table_header_style),
                        Paragraph("Операционная система", table_header_style)
                    ]]
                    
                    for host in hosts:
                        host_data.append([
                            Paragraph(host.ip, table_cell_style),
                            Paragraph(host.mac or "Нет", table_cell_style),
                            Paragraph(host.os or "Нет", table_cell_style)
                        ])
                    
                    host_table = Table(host_data, colWidths=[150, 150, 200])
                    host_table.setStyle(TableStyle([
                        ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor("#4A6F9E")),
                        ('BACKGROUND', (0, 1), (-1, -1), colors.HexColor("#E8EDF4")),
                        ('GRID', (0, 0), (-1, -1), 1, colors.black),
                        ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
                    ]))
                    elements.append(host_table)
                    elements.append(Spacer(1, 12))

                    # Порты и сервисы
                    for host in hosts:
                        ports = scanner_service.get_ports_by_host(host.id)
                        if ports:
                            elements.append(Paragraph(f"Порты и сервисы для хоста: {host.ip}", heading4_style))
                            
                            for port in ports:
                                port_info = [
                                    [Paragraph("Порт", table_header_style), Paragraph("Протокол", table_header_style), Paragraph("Состояние", table_header_style)],
                                    [
                                        Paragraph(str(port.port_num), table_cell_style),
                                        Paragraph(port.protocol, table_cell_style),
                                        Paragraph(port.state, table_cell_style)
                                    ]
                                ]
                                port_table = Table(port_info, colWidths=[80, 80, 80])
                                port_table.setStyle(TableStyle([
                                    ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor("#5D7CA6")),
                                    ('BACKGROUND', (0, 1), (-1, -1), colors.HexColor("#F0F5FC")),
                                    ('GRID', (0, 0), (-1, -1), 1, colors.black),
                                ]))
                                elements.append(port_table)
                                
                                services = scanner_service.get_services_by_port(port.id)
                                if services:
                                    elements.append(Paragraph("Сервисы:", heading4_style))
                                    service_data = [[
                                        Paragraph("Название", table_header_style),
                                        Paragraph("Продукт", table_header_style),
                                        Paragraph("Версия", table_header_style),
                                        Paragraph("Доп. информация", table_header_style)
                                    ]]
                                    
                                    for service in services:
                                        service_data.append([
                                            Paragraph(service.name or "Нет", table_cell_style),
                                            Paragraph(service.product or "Нет", table_cell_style),
                                            Paragraph(service.version or "Нет", table_cell_style),
                                            Paragraph(service.extrainfo or "Нет", table_cell_style)
                                        ])
                                    
                                    service_table = Table(service_data, colWidths=[100, 100, 80, 120])
                                    service_table.setStyle(TableStyle([
                                        ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor("#6D8CB8")),
                                        ('BACKGROUND', (0, 1), (-1, -1), colors.HexColor("#F5F8FD")),
                                        ('GRID', (0, 0), (-1, -1), 1, colors.black),
                                    ]))
                                    elements.append(service_table)
                                    elements.append(Spacer(1, 8))

                # Результаты Modbus
                modbus_result = scanner_service.get_modbus_result(scan.id)
                if modbus_result:
                    elements.append(Paragraph("Результаты Modbus сканирования", heading3_style))
                    modbus_data = [
                        [Paragraph("Параметр", table_header_style), Paragraph("Значение", table_header_style)],
                        [Paragraph("Активные Coils", table_header_style), Paragraph(modbus_result.active_coils or "Нет", table_cell_style)],
                        [Paragraph("Активные Discrete Inputs", table_header_style), Paragraph(modbus_result.active_discrete_inputs or "Нет", table_cell_style)],
                        [Paragraph("Активные Holding Registers", table_header_style), Paragraph(modbus_result.active_holding_registers or "Нет", table_cell_style)],
                        [Paragraph("Активные Input Registers", table_header_style), Paragraph(modbus_result.active_input_registers or "Нет", table_cell_style)]
                    ]
                    modbus_table = Table(modbus_data, colWidths=[200, 300])
                    modbus_table.setStyle(TableStyle([
                        ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor("#4A6F9E")),
                        ('BACKGROUND', (0, 1), (-1, -1), colors.HexColor("#E8EDF4")),
                        ('GRID', (0, 0), (-1, -1), 1, colors.black),
                    ]))
                    elements.append(modbus_table)
                
                elements.append(Spacer(1, 24))

        # Атаки
        if attacks:
            elements.append(PageBreak())
            elements.append(Paragraph("Результаты атак", heading1_style))
            
            for attack in attacks:
                elements.append(Paragraph(f"Атака: {attack.tool}", heading2_style))
                attack_data = [
                    [Paragraph("Параметр", table_header_style), Paragraph("Значение", table_header_style)],
                    [Paragraph("ID", table_header_style), Paragraph(str(attack.id), table_cell_style)],
                    [Paragraph("Аргументы", table_header_style), Paragraph(attack.args or "Нет", table_cell_style)],
                    [Paragraph("Сводка", table_header_style), Paragraph(attack.summary or "Нет", table_cell_style)],
                    [Paragraph("Длительность", table_header_style), Paragraph(f"{attack.duration} секунд", table_cell_style)],
                    [Paragraph("Дата создания", table_header_style), Paragraph(str(attack.created_at), table_cell_style)]
                ]
                attack_table = Table(attack_data, colWidths=[150, 350])
                attack_table.setStyle(TableStyle([
                    ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor("#4A6F9E")),
                    ('BACKGROUND', (0, 1), (-1, -1), colors.HexColor("#E8EDF4")),
                    ('GRID', (0, 0), (-1, -1), 1, colors.black),
                    ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
                ]))
                elements.append(attack_table)
                
                # Результаты атак
                results = scanner_service.get_attack_results_by_attack(attack.id)
                if results:
                    elements.append(Paragraph("Результаты:", heading3_style))
                    result_data = [[
                        Paragraph("ID", table_header_style),
                        Paragraph("Сводка", table_header_style)
                    ]]
                    
                    for result in results:
                        result_data.append([
                            Paragraph(str(result.id), table_cell_style),
                            Paragraph(result.summary or "Нет", table_cell_style)
                        ])
                    
                    result_table = Table(result_data, colWidths=[50, 450])
                    result_table.setStyle(TableStyle([
                        ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor("#5D7CA6")),
                        ('BACKGROUND', (0, 1), (-1, -1), colors.HexColor("#F0F5FC")),
                        ('GRID', (0, 0), (-1, -1), 1, colors.black),
                    ]))
                    elements.append(result_table)
                
                elements.append(Spacer(1, 24))

        # Создание PDF
        doc.build(elements)
        logger.info(f"PDF отчет успешно создан: {output_path}")
    except Exception as e:
        logger.error(f"Ошибка при создании PDF отчета: {e}")
        raise

def generate_json_report(scanner_service: ScannerManager, session_id: int) -> str:
    """Генерация JSON отчета для указанной сессии"""
    try:
        session = scanner_service.get_session_by_id(session_id)
        if not session:
            logger.warning(f"Сессия с ID {session_id} не найдена")
            return json.dumps({"error": "Сессия не найдена"})

        scans = scanner_service.get_scans_by_session(session_id)
        scans_data = []
        for scan in scans:
            hosts = scanner_service.get_hosts_by_scan(scan.id)
            hosts_data = []
            for host in hosts:
                ports = scanner_service.get_ports_by_host(host.id)
                ports_data = []
                for port in ports:
                    services = scanner_service.get_services_by_port(port.id)
                    services_data = []
                    for service in services:
                        cpes = scanner_service.get_cpes_by_service(service.id)
                        cpes_data = [{"id": cpe.id, "name": cpe.name} for cpe in cpes]
                        services_data.append({
                            "id": service.id,
                            "name": service.name,
                            "product": service.product,
                            "extrainfo": service.extrainfo,
                            "ostype": service.ostype,
                            "cpes": cpes_data
                        })
                    ports_data.append({
                        "id": port.id,
                        "protocol": port.protocol,
                        "port_num": port.port_num,
                        "state": port.state,
                        "services": services_data
                    })
                hosts_data.append({
                    "id": host.id,
                    "ip": host.ip,
                    "mac": host.mac,
                    "os": host.os,
                    "ports": ports_data
                })
            modbus_result = scanner_service.get_modbus_result(scan.id)
            modbus_data = {
                "active_coils": modbus_result.active_coils if modbus_result else "",
                "active_discrete_inputs": modbus_result.active_discrete_inputs if modbus_result else "",
                "active_holding_registers": modbus_result.active_holding_registers if modbus_result else "",
                "active_input_registers": modbus_result.active_input_registers if modbus_result else ""
            } if modbus_result else None
            scans_data.append({
                "id": scan.id,
                "tool": scan.tool,
                "args": scan.args,
                "summary": scan.summary,
                "duration": scan.duration,
                "created_at": scan.created_at,
                "hosts": hosts_data,
                "modbus_result": modbus_data
            })

        attacks = scanner_service.get_attacks_by_session(session_id)
        attacks_data = []
        for attack in attacks:
            results = scanner_service.get_attack_results_by_attack(attack.id)
            results_data = [{"id": result.id, "summary": result.summary} for result in results]
            attacks_data.append({
                "id": attack.id,
                "tool": attack.tool,
                "args": attack.args,
                "summary": attack.summary,
                "duration": attack.duration,
                "created_at": attack.created_at,
                "results": results_data
            })

        report_data = {
            "session": {
                "id": session.id,
                "name": session.name,
                "created_at": session.created_at
            },
            "scans": scans_data,
            "attacks": attacks_data
        }

        logger.info(f"JSON отчет успешно сгенерирован для сессии {session_id}")
        return json.dumps(report_data, indent=4, ensure_ascii=False)
    except Exception as e:
        logger.error(f"Ошибка при генерации JSON отчета: {e}")
        return json.dumps({"error": str(e)})