"""Stage 1c tests — DXF and PDF exporters.

DXF is asserted by a binary round-trip (re-read with ezdxf, check layers/entities).
The PDF render is skipped when WeasyPrint's native libraries are unavailable (dev
boxes без libgobject); the HTML-build step is always exercised.
"""

from __future__ import annotations

import importlib.util
import os
import tempfile
from collections import Counter

import ezdxf
import pytest

from app.schemas.concept import ConceptInput
from app.services.generative import geometry
from app.services.generative.exporters import dxf, pdf

_PARCEL_COORDS = [
    [60.60, 56.830],
    [60.6045, 56.830],
    [60.6045, 56.8328],
    [60.60, 56.8328],
    [60.60, 56.830],
]


def _payload() -> ConceptInput:
    return ConceptInput(
        parcel_geojson={"type": "Polygon", "coordinates": [_PARCEL_COORDS]},
        housing_class="comfort",
        target_floors=9,
        development_type="mid_rise",
        land_cost_rub=150_000_000.0,
    )


def _weasyprint_available() -> bool:
    """WeasyPrint импортируется только с нативными библиотеками (libgobject и т.д.)."""
    if importlib.util.find_spec("weasyprint") is None:
        return False
    try:
        import weasyprint  # noqa: F401
    except OSError:
        return False
    return True


def test_dxf_export_round_trips_with_layers() -> None:
    payload = _payload()
    parcel = geometry.parse_parcel(payload)
    variant = geometry.generate(payload)[0]

    data = dxf.export_concept_dxf(parcel, variant)
    assert data.startswith(b"AutoCAD Binary DXF")

    with tempfile.NamedTemporaryFile(suffix=".dxf", delete=False) as fh:
        fh.write(data)
        path = fh.name
    try:
        doc = ezdxf.readfile(path)
    finally:
        os.unlink(path)

    msp = doc.modelspace()
    by_layer = Counter(e.dxf.layer for e in msp)
    # Участок и пятно застройки нарисованы.
    assert by_layer["PARCEL"] == 1
    assert by_layer["BUILDABLE"] == 1
    # Секции нарисованы (по одному polyline на секцию + подписи).
    n_sections = len(variant.buildings_geojson["features"])
    assert n_sections > 0
    assert by_layer["BUILDINGS"] >= n_sections


def test_dxf_building_footprints_have_metric_area() -> None:
    from shapely.geometry import Polygon

    payload = _payload()
    parcel = geometry.parse_parcel(payload)
    variant = geometry.generate(payload)[0]
    data = dxf.export_concept_dxf(parcel, variant)

    with tempfile.NamedTemporaryFile(suffix=".dxf", delete=False) as fh:
        fh.write(data)
        path = fh.name
    try:
        doc = ezdxf.readfile(path)
    finally:
        os.unlink(path)

    areas = []
    for e in doc.modelspace():
        if e.dxftype() == "LWPOLYLINE" and e.dxf.layer == "BUILDINGS":
            pts = [(p[0], p[1]) for p in e.get_points()]
            areas.append(Polygon(pts).area)
    assert areas, "no building polylines found"
    # Площади секций — десятки/сотни кв.м (метры), не доли (градусы).
    for area in areas:
        assert 50.0 < area < 2000.0


def test_pdf_html_build_contains_tables() -> None:
    variants = geometry.generate(_payload())
    html = pdf._build_html(variants)
    assert "Технико-экономические показатели" in html
    assert "Финансовая модель" in html
    # PR-3: DCF-строки заменили IRR-proxy в таблице финмодели.
    assert "NPV (DCF), млн руб" in html
    assert "IRR (DCF, годовой)" in html
    assert "Окупаемость (PBP)" in html


def test_pdf_html_build_graceful_on_empty() -> None:
    html = pdf._build_html([])
    assert "нет вариантов" in html


def test_pdf_caveat_does_not_contradict_office_revenue_row() -> None:
    """Эпик #2445 C1: каскад включает нежилую выручку/НДС — caveat не должен это отрицать.

    Payload — ``comfort`` (office_share=0.05 в teap.py), поэтому строка "Выручка —
    нежилое (1-й этаж)" в таблице ненулевая: caveat ниже НЕ может утверждать, что
    коммерция/офисы "не учитываются" или что НДС — "только машиноместа".
    """
    variants = geometry.generate(_payload())
    html = pdf._build_html(variants)
    assert "Выручка — нежилое (1-й этаж), млн руб" in html
    assert "НДС (нежилое: паркинг + коммерция), млн руб" in html
    # Старые (стейл) формулировки, отрицающие модель нежилого, — не должны вернуться.
    assert "не учитываются" not in html
    assert "только на нежилую часть" not in html
    assert "только машиноместа" not in html
    # Caveat честно описывает нежилое как учтённое (не «нет в ТЭП»).
    assert "нет в ТЭП" not in html


@pytest.mark.skipif(
    not _weasyprint_available(),
    reason="WeasyPrint native libs unavailable on this host",
)
def test_pdf_export_produces_pdf_bytes() -> None:
    variants = geometry.generate(_payload())
    data = pdf.export_concept_pdf(variants)
    assert data.startswith(b"%PDF-")
    assert len(data) > 1000
