import base64
import io
from datetime import datetime

from fpdf import FPDF

_COL_LABEL = 80
_COL_VALUE = 50
_ROW_H = 7


def generate_pdf(overall: dict, group_stats: list, charts: list, overview_chart: str) -> bytes:
    pdf = FPDF()
    pdf.set_auto_page_break(auto=True, margin=15)

    pdf.add_page()
    _title_block(pdf)
    _overall_section(pdf, overall, overview_chart)

    for stat, chart_b64 in zip(group_stats, charts):
        _group_section(pdf, stat, chart_b64)

    return bytes(pdf.output())


# ---------------------------------------------------------------------------

def _title_block(pdf: FPDF):
    pdf.set_font("Helvetica", "B", 18)
    pdf.cell(0, 12, "Ballistic Analysis Report", new_x="LMARGIN", new_y="NEXT", align="C")
    pdf.set_font("Helvetica", "", 9)
    pdf.cell(
        0, 5,
        f"Generated: {datetime.now().strftime('%Y-%m-%d  %H:%M')}",
        new_x="LMARGIN", new_y="NEXT", align="C",
    )
    pdf.ln(8)


def _overall_section(pdf: FPDF, overall: dict, overview_chart: str):
    _section_heading(pdf, "Overall Statistics")
    rows = [
        ("Total shots",    str(overall["count"])),
        ("Min speed",      f"{overall['min_speed']:.4f}"),
        ("Max speed",      f"{overall['max_speed']:.4f}"),
        ("Mean speed",     f"{overall['mean_speed']:.4f}"),
        ("Std dev (speed)", f"{overall['std_speed']:.4f}" if overall["std_speed"] is not None else "n/a"),
    ]
    _table(pdf, rows)
    img_bytes = base64.b64decode(overview_chart)
    pdf.image(io.BytesIO(img_bytes), x=pdf.l_margin, w=min(140, pdf.epw))
    pdf.ln(4)


def _group_section(pdf: FPDF, stat: dict, chart_b64: str):
    pdf.ln(4)
    heading = (
        f"Group {stat['group_index']}  -  "
        f"{stat['time_start']} to {stat['time_end']}  "
        f"({stat['count']} shot(s))"
    )
    _section_heading(pdf, heading)

    rows = [
        ("Min speed",      f"{stat['min_speed']:.4f}"),
        ("Max speed",      f"{stat['max_speed']:.4f}"),
        ("Mean speed",     f"{stat['mean_speed']:.4f}"),
        ("Std dev (speed)", f"{stat['std_speed']:.4f}" if stat["std_speed"] is not None else "n/a"),
    ]
    _table(pdf, rows)

    img_bytes = base64.b64decode(chart_b64)
    # Check remaining page space; add new page if chart won't fit
    if pdf.get_y() + 75 > pdf.page_break_trigger:
        pdf.add_page()
    pdf.image(io.BytesIO(img_bytes), x=pdf.l_margin, w=pdf.epw)
    pdf.ln(4)


def _section_heading(pdf: FPDF, text: str):
    pdf.set_font("Helvetica", "B", 12)
    pdf.set_fill_color(230, 236, 255)
    pdf.cell(0, 8, text, new_x="LMARGIN", new_y="NEXT", fill=True)
    pdf.ln(2)


def _table(pdf: FPDF, rows: list):
    for i, (label, value) in enumerate(rows):
        fill = i % 2 == 0
        pdf.set_fill_color(248, 249, 252) if fill else pdf.set_fill_color(255, 255, 255)
        pdf.set_font("Helvetica", "", 10)
        pdf.cell(_COL_LABEL, _ROW_H, label, border=0, fill=fill)
        pdf.set_font("Helvetica", "B", 10)
        pdf.cell(_COL_VALUE, _ROW_H, value, border=0, fill=fill, new_x="LMARGIN", new_y="NEXT")
    pdf.ln(3)
