from __future__ import annotations

from pathlib import Path
from urllib.parse import quote

from app.core.config import settings
from app.modules.orion.assets import schubamed_logo_uri
from app.modules.orion.components.base import ReportComponent
from app.modules.orion.context import ReportContext
from app.modules.orion.html import definition_list, paragraph, section, table, text, yes_no


def render_template_text(content: str, context: ReportContext) -> str:
    values = {
        "device.manufacturer": context.device.manufacturer if context.device else None,
        "device.model": context.device.model if context.device else None,
        "device.serial_number": context.device.serial_number if context.device else None,
        "customer.name": context.customer.name,
        "location.city": context.location.city if context.location else None,
        "validation.performed_on": context.validation.performed_on,
        "validation.next_validation_on": context.validation.next_validation_on,
        "validation.result": context.validation.result,
    }
    rendered = content
    for key, value in values.items():
        rendered = rendered.replace("{{ " + key + " }}", text(value))
    return rendered


class NumberedComponent(ReportComponent):
    anchor = ""
    title = ""

    def __init__(self, anchor: str | None = None, title: str | None = None, number: str | None = None) -> None:
        if anchor is not None:
            self.anchor = anchor
        if title is not None:
            self.title = title
        self.number = number

    @property
    def bookmark_label(self) -> str:
        return f"{self.number} {self.title}" if self.number else self.title


class CoverComponent(ReportComponent):
    anchor = "cover"
    title = "Deckblatt"

    def render(self, context: ReportContext) -> str:
        validation = context.validation
        rows = definition_list(
            [
                ("Hersteller", context.device.manufacturer if context.device else "nicht erfasst"),
                ("Geraet", context.device.model if context.device else "nicht erfasst"),
                (
                    "Seriennummer",
                    context.device.serial_number if context.device else "nicht erfasst",
                ),
                ("Berichtsnummer", validation.report_number),
                ("Validierungsart", validation.validation_type),
                ("Projekt", validation.project),
                ("Pruefdatum", validation.performed_on),
                ("Pruefungsort", validation.test_location),
                ("Pruefer", validation.examiner_name),
                ("Gesamtergebnis", validation.result),
                ("Status", validation.status),
                (
                    "Ansprechpartner",
                    context.contact.full_name if context.contact else "nicht erfasst",
                ),
                ("Mitwirkende Personen", validation.participants or "nicht erfasst"),
            ]
        )
        logo_uri = schubamed_logo_uri()
        return (
            '<section class="cover-page" id="cover">'
            '<div class="cover-top">'
            '<div class="company-address">SCHUBAMED<br>Validation Suite<br>Medizintechnik und Validierung</div>'
            f'<img class="cover-logo" src="{logo_uri}" alt="SCHUBAMED Validation Suite">'
            "</div>"
            "<h1>PRÜFBERICHT ZUR VALIDIERUNG</h1>"
            '<p class="cover-subtitle">Funktions- und Leistungsqualifikation Klein-Sterilisator</p>'
            f'<p class="cover-subtitle">{text(context.customer.name)}</p>'
            f"{rows}"
            '<div class="signature-grid"><div>Unterschrift technische Validierung</div><div>Unterschrift Auftraggeber</div></div>'
            "</section>"
        )


class TocComponent(ReportComponent):
    anchor = "toc"
    title = "Inhaltsverzeichnis"

    def __init__(self, sections: list[dict]) -> None:
        self.sections = sections

    def render(self, context: ReportContext) -> str:
        links = "".join(
            self._toc_item(item)
            for item in self.sections
            if item.get("toc")
        )
        return section(self.anchor, self.title, f'<ol class="toc-list">{links}</ol>')

    def _toc_item(self, item: dict) -> str:
        number = item.get("number")
        title = item.get("title")
        label = f"{number} {title}" if number else str(title)
        depth = str(number).count(".") + 1 if number else 2
        return (
            f'<li class="toc-level-{depth}"><a href="#{text(item.get("key"))}">'
            f'<span class="toc-label">{text(label)}</span></a></li>'
        )


class SummaryComponent(ReportComponent):
    anchor = "summary"
    title = "Zusammenfassendes Ergebnis der Validierung"

    def render(self, context: ReportContext) -> str:
        validation = context.validation
        block = context.text_blocks.get("summary")
        body = f"<p>{paragraph(render_template_text(block.content, context) if block else 'nicht erfasst')}</p>"
        body += definition_list(
            [
                ("Kunde", context.customer.name),
                ("Standort", context.location.name if context.location else None),
                (
                    "Geraet",
                    (
                        f"{context.device.manufacturer} {context.device.model}"
                        if context.device
                        else None
                    ),
                ),
                ("Pruefmittel", len(context.equipment)),
                ("Ergebnis", validation.result),
                ("Mitwirkende Personen", validation.participants),
                (
                    "Hinweis auf naechste Leistungsbeurteilung",
                    validation.next_validation_on or "nicht erfasst",
                ),
            ]
        )
        return section(self.anchor, self.title, body)


class StaticTextComponent(ReportComponent):
    def __init__(
        self,
        anchor: str,
        title: str,
        body: str = "nicht erfasst",
        block_key: str | None = None,
        number: str | None = None,
    ) -> None:
        self.anchor = anchor
        self.title = title
        self.body = body
        self.block_key = block_key
        self.number = number

    @property
    def bookmark_label(self) -> str:
        return f"{self.number} {self.title}" if self.number else self.title

    def render(self, context: ReportContext) -> str:
        if self.block_key and self.block_key in context.text_blocks:
            content = render_template_text(context.text_blocks[self.block_key].content, context)
            return section(self.anchor, self.title, f"<p>{paragraph(content)}</p>", self.bookmark_label)
        return section(self.anchor, self.title, f"<p>{paragraph(self.body)}</p>", self.bookmark_label)


class CustomerComponent(ReportComponent):
    anchor = "customer"
    title = "Kunde, Standort und Ansprechpartner"

    def render(self, context: ReportContext) -> str:
        customer = context.customer
        location = context.location
        contact = context.contact
        body = "<h3>Kunde</h3>" + definition_list(
            [
                ("Name", customer.name),
                ("Typ", customer.customer_type.value),
                (
                    "Adresse",
                    " ".join(filter(None, [customer.street, customer.postal_code, customer.city])),
                ),
                ("Telefon", customer.phone),
                ("Mail", customer.email),
                ("Betreiber", context.validation.operator_name),
                ("QM", customer.quality_manager),
                ("Hygienebeauftragter", customer.hygiene_officer),
            ]
        )
        if location:
            body += "<h3>Standort</h3>" + definition_list(
                [
                    ("Name", location.name),
                    (
                        "Adresse",
                        " ".join(
                            filter(None, [location.street, location.postal_code, location.city])
                        ),
                    ),
                    ("Raum", location.room),
                ]
            )
        if contact:
            body += "<h3>Ansprechpartner</h3>" + definition_list(
                [
                    ("Name", contact.full_name),
                    ("Funktion", contact.function),
                    ("Mail", contact.email),
                    ("Telefon", contact.phone),
                ]
            )
        return section(self.anchor, self.title, body)


class DeviceComponent(NumberedComponent):
    anchor = "device"
    title = "Geraet"

    def render(self, context: ReportContext) -> str:
        device = context.device
        if device is None:
            return section(self.anchor, self.title, "<p>nicht erfasst</p>", self.bookmark_label)
        body = definition_list(
            [
                ("Hersteller", device.manufacturer),
                ("Modell", device.model),
                ("Typ", device.device_type),
                ("Seriennummer", device.serial_number),
                ("Baujahr", device.year_built),
                ("Inbetriebnahme", device.commissioned_on),
                (
                    "Kammervolumen",
                    (
                        f"{device.chamber_volume_liters} Liter"
                        if device.chamber_volume_liters
                        else None
                    ),
                ),
                ("Dampferzeugung", device.steam_generation),
                ("Wasseraufbereitung", device.water_treatment),
                ("Dokumentation", device.documentation),
                ("Lieferant", device.supplier),
            ]
        )
        return section(self.anchor, self.title, body, self.bookmark_label)


class EquipmentComponent(NumberedComponent):
    anchor = "equipment"
    title = "Pruefmittel"

    def render(self, context: ReportContext) -> str:
        rows = [
            [
                item.kind.value,
                item.manufacturer,
                item.model,
                item.serial_number,
                item.calibrated_on,
                item.calibration_due_on,
                item.status.value,
            ]
            for item in context.equipment
        ]
        return section(
            self.anchor,
            self.title,
            table(
                [
                    "Art",
                    "Hersteller",
                    "Modell",
                    "Seriennummer",
                    "Kalibriert",
                    "Gueltig bis",
                    "Status",
                ],
                rows,
            ),
            self.bookmark_label,
        )


class EnvironmentComponent(NumberedComponent):
    anchor = "environment"
    title = "Umgebungsbedingungen"

    def render(self, context: ReportContext) -> str:
        data = context.validation.environment_conditions or {}
        body = definition_list(
            [
                ("Raumtemperatur", data.get("room_temperature")),
                ("relative Luftfeuchtigkeit", data.get("humidity")),
                ("Pruefzeit", data.get("test_time")),
            ]
        )
        rows = [
            [item.get("text"), yes_no(item.get("value")), item.get("comment")]
            for item in data.get("checks", [])
        ]
        if rows:
            body += table(["Pruefpunkt", "Bewertung", "Kommentar"], rows)
        return section(self.anchor, self.title, body, self.bookmark_label)


class ChecklistComponent(NumberedComponent):
    anchor = "checklists"
    title = "Dokumentations- und Leistungschecklisten"

    def render(self, context: ReportContext) -> str:
        body = ""
        for checklist in context.checklist_templates:
            body += f"<h3>{text(checklist.title)}</h3>"
            body += self._render_items(checklist.items)
        if not body:
            documentation = context.validation.documentation_checklist or []
            performance = context.validation.performance_checklist or []
            body = "<h3>Dokumentation</h3>" + self._render_items(documentation)
            body += "<h3>Leistung</h3>" + self._render_items(performance)
        return section(self.anchor, self.title, body, self.bookmark_label)

    def _render_items(self, items: list[dict]) -> str:
        rows = [
            [item.get("number"), item.get("text"), yes_no(item.get("value")), item.get("comment")]
            for item in items
        ]
        return table(["Nr.", "Pruefpunkt", "Bewertung", "Kommentar"], rows)


class ProgramComponent(NumberedComponent):
    anchor = "programs"
    title = "Programme"

    def render(self, context: ReportContext) -> str:
        programs = [item for item in (context.validation.programs or []) if item.get("selected")]
        rows = [
            [index + 1, item.get("name"), "Eigenes Programm" if item.get("custom") else "Standard"]
            for index, item in enumerate(programs)
        ]
        return section(self.anchor, self.title, table(["Nr.", "Programm", "Typ"], rows), self.bookmark_label)


class LoadingComponent(NumberedComponent):
    anchor = "loading"
    title = "Beladungsmuster"

    def render(self, context: ReportContext) -> str:
        rows = [
            [
                item.get("run"),
                item.get("pattern"),
                item.get("description"),
                len(item.get("images", [])),
            ]
            for item in context.validation.loading_patterns or []
        ]
        return section(
            self.anchor,
            self.title,
            table(["Testlauf", "Beladungsmuster", "Beschreibung", "Bilder"], rows),
            self.bookmark_label,
        )


class MeasurementComponent(NumberedComponent):
    anchor = "measurements"
    title = "Messdaten"

    def render(self, context: ReportContext) -> str:
        if context.confirmed_measurements:
            rows = [
                [
                    item.test_run,
                    item.field_name,
                    item.corrected_value or item.normalized_value or item.raw_value,
                    item.unit,
                    item.source_page,
                    f"{item.confidence} %",
                ]
                for item in context.confirmed_measurements
            ]
            return section(
                self.anchor,
                self.title,
                table(["Testlauf", "Messwert", "Wert", "Einheit", "Quelle", "Sicherheit"], rows, "compact"),
                self.bookmark_label,
            )
        rows = [
            [
                item.get("name"),
                item.get("start_time"),
                item.get("end_time"),
                item.get("duration"),
                item.get("leak_rate"),
                item.get("min_temperature"),
                item.get("max_temperature"),
                item.get("temperature_band"),
                item.get("holding_time"),
                item.get("pressure"),
                item.get("result"),
            ]
            for item in context.validation.measurement_data or []
        ]
        body = table(
            [
                "Bereich",
                "Start",
                "Ende",
                "Dauer",
                "Leckrate",
                "Min. Temp.",
                "Max. Temp.",
                "Band",
                "Haltezeit",
                "Druck",
                "Ergebnis",
            ],
            rows,
            "compact",
        )
        winlog_rows = []
        for item in context.validation.measurement_data or []:
            for imported in item.get("imports", []):
                winlog_rows.append(
                    [item.get("name"), imported.get("filename"), imported.get("content_type")]
                )
        if winlog_rows:
            body += "<h3>Winlog-Dateien</h3>" + table(["Bereich", "Datei", "Typ"], winlog_rows)
        body += "<p>Messdaten noch nicht bestätigt.</p>"
        return section(self.anchor, self.title, body, self.bookmark_label)


class DryingComponent(NumberedComponent):
    anchor = "drying"
    title = "Trocknung"

    def render(self, context: ReportContext) -> str:
        data = context.validation.drying or {}
        body = definition_list(
            [
                ("Startgewicht", data.get("start_weight")),
                ("Endgewicht", data.get("end_weight")),
                ("Differenz", data.get("difference")),
                ("Bewertung", data.get("rating")),
                ("Bemerkung", data.get("comment")),
            ]
        )
        return section(self.anchor, self.title, body, self.bookmark_label)


class RecommendationComponent(NumberedComponent):
    anchor = "recommendations"
    title = "Empfehlungen und Auflagen"

    def render(self, context: ReportContext) -> str:
        rows = [
            [item.get("number"), item.get("text"), item.get("deadline"), item.get("status")]
            for item in context.validation.recommendations or []
        ]
        return section(self.anchor, self.title, table(["Nr.", "Text", "Frist", "Status"], rows), self.bookmark_label)


class AttachmentComponent(NumberedComponent):
    anchor = "attachments"
    title = "Bilder und Anlagen"

    def render(self, context: ReportContext) -> str:
        attachments = sorted(
            context.validation.attachments or [], key=lambda row: row.get("order") or 0
        )
        rows = [
            [item.get("order"), item.get("category"), item.get("filename"), item.get("description")]
            for item in attachments
        ]
        figures = []
        for index, item in enumerate(attachments, start=1):
            src = self._image_source(item)
            if not src:
                continue
            caption = (
                item.get("description") or item.get("filename") or item.get("category") or "Anlage"
            )
            figures.append(
                '<figure class="report-figure">'
                f'<img class="report-image" src="{text(src)}" alt="{text(caption)}">'
                f"<figcaption>Abbildung {index}: {text(caption)}</figcaption>"
                "</figure>"
            )
        body = table(["Reihenfolge", "Kategorie", "Datei", "Beschreibung"], rows)
        if figures:
            body += '<div class="figure-grid">' + "".join(figures) + "</div>"
        return section(self.anchor, self.title, body, self.bookmark_label)

    def _image_source(self, item: dict) -> str | None:
        content_type = str(item.get("content_type") or "")
        filename = str(item.get("filename") or "")
        if not content_type.startswith("image/") and Path(filename).suffix.lower() not in {
            ".jpg",
            ".jpeg",
            ".png",
            ".webp",
            ".svg",
        }:
            return None
        url = item.get("url")
        if isinstance(url, str) and url:
            if url.startswith(("http://", "https://")):
                return url
            return f"{settings.public_base_url.rstrip('/')}{quote(url, safe='/:._-')}"
        storage_path = item.get("storage_path")
        if storage_path:
            path = Path(str(storage_path))
            if path.exists():
                return path.resolve().as_uri()
        return None
