from __future__ import annotations

from pathlib import Path
import logging

from sqlalchemy.orm import Session

from app.modules.orion.assets import ORION_ASSET_DIR
from app.modules.orion.components import (
    AttachmentComponent,
    ChecklistComponent,
    CoverComponent,
    CustomerComponent,
    DeviceComponent,
    DryingComponent,
    EnvironmentComponent,
    EquipmentComponent,
    LoadingComponent,
    MeasurementComponent,
    ProgramComponent,
    RecommendationComponent,
    ReportComponent,
    StaticTextComponent,
    SummaryComponent,
    TocComponent,
)
from app.modules.orion.context import OrionContextBuilder, ReportContext
from app.modules.orion.templates.report import render_document
from app.modules.orion.template_service import REPORT_SECTIONS

logger = logging.getLogger(__name__)


class OrionReportService:
    def __init__(self, session: Session, generated_dir: Path | None = None) -> None:
        self.session = session
        self.generated_dir = generated_dir or Path("/app/reports")

    def render_html(self, validation_id: str) -> str:
        context = OrionContextBuilder(self.session, self.generated_dir).build(validation_id)
        components = self._components()
        chapters = [component.render(context) for component in components]
        return render_document(context, chapters)

    def render_pdf(self, validation_id: str) -> Path:
        from weasyprint import HTML

        context = OrionContextBuilder(self.session, self.generated_dir).build(validation_id)
        output_path = context.generated_dir / f"{context.validation.report_number}.pdf"
        html = self.render_html(validation_id)
        HTML(string=html, base_url=str(ORION_ASSET_DIR)).write_pdf(output_path)
        self._append_pdf_attachments(context, output_path)
        return output_path

    def _components(self) -> list[ReportComponent]:
        chapters = [self._component_for_section(item) for item in REPORT_SECTIONS]
        return [CoverComponent(), SummaryComponent(), TocComponent(REPORT_SECTIONS), CustomerComponent(), *chapters]

    def _component_for_section(self, item: dict) -> ReportComponent:
        component = item.get("component")
        key = str(item["key"])
        title = str(item["title"])
        number = item.get("number")
        if component == "device":
            return DeviceComponent(key, title, number)
        if component == "checklist":
            return ChecklistComponent(key, title, number)
        if component == "environment":
            return EnvironmentComponent(key, title, number)
        if component == "programs":
            return ProgramComponent(key, title, number)
        if component == "loading":
            return LoadingComponent(key, title, number)
        if component == "equipment":
            return EquipmentComponent(key, title, number)
        if component == "measurements":
            return MeasurementComponent(key, title, number)
        if component == "drying":
            return DryingComponent(key, title, number)
        if component == "recommendations":
            return RecommendationComponent(key, title, number)
        if component == "attachments":
            return AttachmentComponent(key, title, number)
        return StaticTextComponent(key, title, block_key=item.get("block_key"), number=number)

    def _append_pdf_attachments(self, context: ReportContext, output_path: Path) -> None:
        pdf_paths = []
        for item in context.validation.attachments or []:
            filename = str(item.get("filename") or "")
            content_type = str(item.get("content_type") or "")
            if not filename.lower().endswith(".pdf") and content_type != "application/pdf":
                continue
            storage_path = item.get("storage_path")
            if storage_path and Path(str(storage_path)).exists():
                pdf_paths.append(Path(str(storage_path)))
        if not pdf_paths:
            return
        try:
            from pypdf import PdfReader, PdfWriter

            writer = PdfWriter()
            for page in PdfReader(str(output_path)).pages:
                writer.add_page(page)
            for pdf_path in pdf_paths:
                try:
                    for page in PdfReader(str(pdf_path)).pages:
                        writer.add_page(page)
                except Exception:
                    logger.exception("Could not append attachment PDF %s", pdf_path)
            with output_path.open("wb") as handle:
                writer.write(handle)
        except Exception:
            logger.exception("Could not merge Orion PDF attachments for validation %s", context.validation.id)
