"""The export writers, asserted against the file read back. A cell's type
and number format are the thing under test and neither is visible in the
code that sets them, so the workbook is reopened and each cell inspected.
The text file is asserted on its raw lines, where quoting and prefixing are
the encoding."""

import csv
from decimal import Decimal
from io import BytesIO

import openpyxl
import pytest

from quber.playground.batch import BatchRow, RunState
from quber.playground.export import (
    COLUMNS,
    NOT_IN_DOCUMENT,
    build_rows,
    percent_format,
    write_csv,
    write_xlsx,
)


def scalar_response(printed, number, refs=None):
    return {
        "shape": "scalar",
        "answer": printed,
        "value": {"value": printed, "number": number, "unit": "percent", "period": "Q1"},
        "references": refs or [],
    }


REFS = [
    {
        "ref_id": "t1-2-3",
        "page": 21,
        "text": "6.7",
        "status": "reconciled",
        "note": None,
        "flagged": False,
    },
    {
        "ref_id": "t1-2-4",
        "page": 21,
        "text": "6.8",
        "status": "unverified",
        "note": "could not read",
        "flagged": True,
    },
]


def run_state(rows):
    return RunState(
        job_id="j",
        doc_id=1,
        want="value",
        total=len(rows),
        done=len(rows),
        failed=sum(1 for r in rows if r.error),
        running=False,
        rows=rows,
    )


STATE = run_state(
    [
        BatchRow(index=0, question="coupon?", response=scalar_response("6.7 %", "6.7", REFS)),
        BatchRow(index=1, question="distributions?", response=scalar_response("$ (10,550)", "-10550")),
        BatchRow(
            index=2,
            question="=SUM(A1:A9)?",
            response={
                "shape": "unanswerable",
                "answer": "n/a",
                "value": {"reason": "not printed"},
                "references": [],
            },
        ),
        BatchRow(index=3, question="why?", error="529 overloaded"),
    ]
)


def test_rows_carry_provenance_and_review_flag():
    rows = build_rows(STATE)
    first = rows[0]
    assert first.printed == "6.7 %" and first.number == Decimal("6.7") and first.percent
    assert first.pages == "21"
    assert first.cited_ids == "t1-2-3; t1-2-4"
    assert first.cell_text == "6.7; 6.8"
    assert first.status == "reconciled; unverified"
    assert first.note == "could not read"
    assert first.review is True
    assert rows[2].printed == NOT_IN_DOCUMENT and rows[2].note == "not printed"
    assert rows[3].error == "529 overloaded" and rows[3].printed == ""


def test_percent_format_mirrors_printed_precision():
    assert percent_format("6.7 %") == "0.0%"
    assert percent_format("7.84%") == "0.00%"
    assert percent_format("65%") == "0%"


def test_xlsx_read_back():
    ws = openpyxl.load_workbook(BytesIO(write_xlsx(STATE))).worksheets[0]
    header = [c.value for c in ws[1]]
    assert header == COLUMNS

    printed = [ws.cell(row=r, column=2) for r in range(2, 5)]
    # The as-printed column is text: nothing reformats it, and a leading
    # formula character is characters. The error row's printed cell is blank.
    assert all(c.data_type == "s" and c.number_format == "@" for c in printed)
    assert printed[0].value == "6.7 %"
    assert printed[1].value == "$ (10,550)"
    assert ws.cell(row=5, column=2).value is None

    # The writer stores exact decimals; openpyxl reads numeric cells back as
    # floats, so the comparison happens on the read-back type.
    percent_cell = ws.cell(row=2, column=3)
    assert percent_cell.value == pytest.approx(0.067)
    assert percent_cell.number_format == "0.0%"
    negative_cell = ws.cell(row=3, column=3)
    assert negative_cell.value == -10550
    assert negative_cell.number_format == "General"
    assert ws.cell(row=4, column=3).value is None

    question_cell = ws.cell(row=4, column=1)
    assert question_cell.value == "=SUM(A1:A9)?" and question_cell.data_type == "s"

    assert ws.cell(row=2, column=11).value == "review"
    assert ws.cell(row=3, column=11).value is None


def test_csv_quoting_and_prefixing():
    text = write_csv(STATE)
    lines = text.split("\r\n")
    assert lines[0] == ",".join(COLUMNS)

    # The printed figure is quoted and never prefixed; the numeric column is
    # bare, keeping its percent sign for the importer to divide.
    assert '"6.7 %",6.7%,' in lines[1]
    assert '"$ (10,550)",-10550,' in lines[2]

    # A free-text cell beginning with a formula character is prefixed.
    assert lines[3].startswith('"\'=SUM(A1:A9)?"')

    # Parsed back as CSV, every row has every column.
    rows = list(csv.reader([line for line in lines if line]))
    assert all(len(r) == len(COLUMNS) for r in rows)
    assert rows[1][10] == "review" and rows[2][10] == ""
    assert rows[4][11] == "529 overloaded"
