# Copyright (c) 2026 Luigi Paone <ppc.luigi.paone@gmail.com>
# SPDX-License-Identifier: BSD-3-Clause

"""Full document generation tests for typst reports."""

import json
from pathlib import Path

from pycivil.EXAStructural.codes import Code
from pycivil.EXAStructural.loads import ForcesOnSection
from pycivil.EXAStructural.loads import Frequency_Enum as Fr
from pycivil.EXAStructural.loads import LimiteState_Enum as Ls
from pycivil.EXAStructural.materials import Concrete, ConcreteSteel
from pycivil.EXAStructural.rcrecsolver.srvRcSecCheck import ModelInputRcSecCheck
from pycivil.EXAStructural.templateRCRect import SectionPlot
from pycivil.EXAUtils.typstReportMakers import (
    ConcreteTypstFB,
    ConcreteSectionTypstFB,
    ForcesOnSectionListTypstFB,
    SteelConcreteTypstFB,
)
from pycivil.EXAUtils.report import (
    EnumFBSection,
    TypstReporter,
    ReportProperties,
    ReportTemplateEnum,
    getTypstTemplatesPath,
)


def _create_section_png(iData: ModelInputRcSecCheck, path: Path) -> None:
    """Create section plot PNG for Typst templates."""
    section = iData.buildRcsRectangular()
    if section is not None:
        plotter = SectionPlot(section)
        plotter.plot()
        plotter.save(path / "section.png")


def test_report_typst_full_document(tmp_path: Path):
    """Test full engineering calculation document generation."""
    code = Code("NTC2018")
    typstTemplatePath = getTypstTemplatesPath()

    # Materials
    concrete = Concrete()
    concrete.setByCode(codeObj=code, catstr="C25/30")
    concreteFB = ConcreteTypstFB(typstTemplatePath, concrete)
    concreteFB.setFragmentOptions({
        "section_title": "Materiale: Calcestruzzo",
        "section_level": EnumFBSection.SEC_SECTION
    })
    f_concrete = concreteFB.buildFragment()

    steel = ConcreteSteel()
    steel.setByCode(code, "B450C")
    steelFB = SteelConcreteTypstFB(typstTemplatePath, steel)
    steelFB.setFragmentOptions({
        "section_title": "Materiale: Acciaio",
        "section_level": EnumFBSection.SEC_SECTION
    })
    f_steel = steelFB.buildFragment()

    # Section
    with open(Path(__file__).parent / "test_report_latex_sections_01.json") as jsonFile:
        jsonObject = json.load(jsonFile)
    iData = ModelInputRcSecCheck(**jsonObject)
    section = iData.buildRcsRectangular()

    f_section = None
    if section is not None:
        # Create section PNG
        _create_section_png(iData, tmp_path)

        sectionFB = ConcreteSectionTypstFB(typstTemplatePath, rcs=section)
        sectionFB.setFragmentOptions({
            "section_title": "Geometria della sezione",
            "section_level": EnumFBSection.SEC_SECTION
        })
        f_section = sectionFB.buildFragment()

    # Loads
    loadsFB = ForcesOnSectionListTypstFB(typstTemplatePath)
    loadsFB.setFragmentOptions({
        "section_title": "Combinazioni di carico",
        "section_level": EnumFBSection.SEC_SECTION
    })
    listOfForces = loadsFB.forces()
    listOfForces.append(
        ForcesOnSection(
            Fx=0, Fy=0, Fz=1e3 * 500,
            Mx=0, My=1e3 * 150000, Mz=0,
            descr="SLU - Combinazione 1",
            id=1,
            limitState=Ls.ULTIMATE,
            frequency=Fr.FREQUENCY_ND,
        )
    )
    listOfForces.append(
        ForcesOnSection(
            Fx=0, Fy=0, Fz=1e3 * 350,
            Mx=0, My=1e3 * 100000, Mz=0,
            descr="SLE - Rara",
            id=2,
            limitState=Ls.SERVICEABILITY,
            frequency=Fr.CHARACTERISTIC,
        )
    )
    listOfForces.append(
        ForcesOnSection(
            Fx=0, Fy=0, Fz=1e3 * 300,
            Mx=0, My=1e3 * 80000, Mz=0,
            descr="SLE - Frequente",
            id=3,
            limitState=Ls.SERVICEABILITY,
            frequency=Fr.FREQUENT,
        )
    )
    f_loads = loadsFB.buildFragment()

    # Assemble fragments
    fragments = [f_concrete, f_steel]
    if f_section is not None:
        fragments.append(f_section)
    fragments.append(f_loads)

    # Create reporter with properties
    reporter = TypstReporter(typstTemplatePath)
    reporter.setProperties(ReportProperties(
        project_brief="Test Project - RC Beam Analysis",
        module_name="PyCivil",
        module_version="0.2.20",
        report_designer="Test Engineer",
        report_date="2026-02-18",
        report_time="10:30",
        report_token="TEST-001",
    ))
    reporter.linkFragments(
        template=ReportTemplateEnum.TYP_ENG_CAL,
        fragments=fragments
    )
    reporter.compileDocument(path=str(tmp_path), fileName="full_report", output_format="pdf")

    print(f"Temporary path for test is {tmp_path}")
    assert tmp_path.joinpath("full_report.typ").exists()
    assert tmp_path.joinpath("full_report.pdf").exists()
    assert tmp_path.joinpath("full_report.pdf").stat().st_size > 0

    # Verify content
    content = tmp_path.joinpath("full_report.typ").read_text(encoding="utf-8")
    assert "Test Project" in content
    assert "C25/30" in content or "25.0" in content
    assert "B450" in content or "450" in content


def _make_dummy_logo(path: Path) -> None:
    """Generate a dummy PNG logo with matplotlib, similar in style to the PyCivil logo."""
    import matplotlib.pyplot as plt
    import matplotlib.patches as mpatches

    fig, ax = plt.subplots(figsize=(6.4, 1.6))
    fig.patch.set_facecolor("white")
    ax.set_facecolor("white")
    ax.set_xlim(0, 1)
    ax.set_ylim(0, 1)
    ax.axis("off")

    red = "#a3202a"
    rect = mpatches.FancyBboxPatch(
        (0.02, 0.1), 0.96, 0.85,
        boxstyle="round,pad=0.02",
        linewidth=0,
        facecolor=red,
    )
    ax.add_patch(rect)
    ax.text(
        0.5, 0.55, "LiviCyP",
        ha="center", va="center",
        fontsize=28, fontweight="bold", color="white",
        transform=ax.transAxes,
    )
    ax.text(
        0.5, 0.18, "only for free Structural Engineers",
        ha="center", va="center",
        fontsize=8, color="white",
        transform=ax.transAxes,
    )

    fig.savefig(path, dpi=96, bbox_inches="tight", pad_inches=0.05)
    plt.close(fig)


def test_report_typst_custom_logo(tmp_path: Path):
    """Test that a custom logo is copied to output and used in the report."""
    code = Code("NTC2018")
    typstTemplatePath = getTypstTemplatesPath()

    # Generate a dummy logo with matplotlib
    custom_logo_dir = tmp_path / "assets"
    custom_logo_dir.mkdir()
    custom_logo = custom_logo_dir / "company_logo.png"
    _make_dummy_logo(custom_logo)

    concrete = Concrete()
    concrete.setByCode(codeObj=code, catstr="C25/30")
    concreteFB = ConcreteTypstFB(typstTemplatePath, concrete)

    output_dir = tmp_path / "output"
    output_dir.mkdir()

    reporter = TypstReporter(typstTemplatePath)
    reporter.setProperties(ReportProperties(
        project_brief="Custom Logo Test",
        module_name="PyCivil",
        module_version="0.2.20",
        report_designer="Test Engineer",
        report_date="2026-02-18",
        report_time="10:30",
        report_token="TEST-LOGO",
        report_logo=custom_logo,
    ))
    reporter.linkFragments(
        template=ReportTemplateEnum.TYP_ENG_CAL,
        fragments=[concreteFB.buildFragment()]
    )
    reporter.compileDocument(path=str(output_dir), fileName="logo_report", output_format="pdf")

    assert output_dir.joinpath("logo_report.typ").exists()
    assert output_dir.joinpath("logo_report.pdf").exists()
    assert output_dir.joinpath("logo_report.pdf").stat().st_size > 0
    # Logo must be copied alongside the .typ file
    assert output_dir.joinpath("company_logo.png").exists()
    # Verify the .typ file references the custom logo name
    content = output_dir.joinpath("logo_report.typ").read_text(encoding="utf-8")
    assert "company_logo.png" in content


def test_report_typst_builder_api(tmp_path: Path):
    """Test using builder API instead of fragments list."""
    code = Code("NTC2018")
    typstTemplatePath = getTypstTemplatesPath()

    concrete = Concrete()
    concrete.setByCode(codeObj=code, catstr="C25/30")
    concreteFB = ConcreteTypstFB(typstTemplatePath, concrete)

    steel = ConcreteSteel()
    steel.setByCode(code, "B450C")
    steelFB = SteelConcreteTypstFB(typstTemplatePath, steel)

    reporter = TypstReporter(typstTemplatePath)
    reporter.linkFragments(
        template=ReportTemplateEnum.TYP_ENG_CAL,
        builder=[concreteFB, steelFB]
    )
    reporter.compileDocument(path=str(tmp_path), fileName="builder_report", output_format="pdf")

    assert tmp_path.joinpath("builder_report.typ").exists()
    assert tmp_path.joinpath("builder_report.pdf").exists()
    assert tmp_path.joinpath("builder_report.pdf").stat().st_size > 0
