from __future__ import annotations

import tempfile
import unittest
from datetime import date, timedelta
from decimal import Decimal
from pathlib import Path

from openpyxl import load_workbook

from monthly_reports.contracts import (
    ChannelObservation,
    DailyVersionPin,
    MonthlyFact,
    MonthlySnapshot,
    STANDARD_SHEETS,
)
from monthly_reports.core import build_monthly_report
from monthly_reports.publishing import OpenpyxlWorkbookBuilder


def report_fixture():
    source = MonthlyFact(
        daily_record_id=1,
        daily_version_id=100,
        business_date=date(2026, 7, 8),
        channel_key="QBD",
        arrival=date(2026, 7, 8),
        departure=date(2026, 7, 10),
        nights=2,
        adults=2,
        children=0,
        block_code="=SYN-FORMULA-LIKE",
        no_of_rooms=1,
        company_name="SYN-COMPANY",
        confirmation_no="SYN-CONFIRMATION",
        disp_room_no="SYN-ROOM",
        effective_rate_amount=Decimal("450.50"),
        full_name="SYN-GUEST",
        res_comment="SYN-COMMENT",
        trace_text="",
        products="",
        rate_code="SYN-RATE",
        room_category_label="SYN-TYPE",
        real_price=Decimal("450.50"),
        total_price=Decimal("901.00"),
        kb_amount=None,
    )
    return build_monthly_report(
        2026,
        7,
        date(2026, 7, 8),
        MonthlySnapshot(
            (source,),
            (DailyVersionPin(date(2026, 7, 8), 100),),
            (ChannelObservation(date(2026, 7, 8), 100, "QBD", 1, 1),),
        ),
    )


def large_decimal_report_fixture():
    facts = []
    daily_versions = []
    channel_observations = []
    record_id = 1000
    daily_plan = (
        (date(2026, 8, 1), 501, 28),
        (date(2026, 8, 2), 502, 30),
        (date(2026, 8, 3), 503, 30),
    )
    for business_date, daily_version_id, rows_per_sheet in daily_plan:
        daily_versions.append(DailyVersionPin(business_date, daily_version_id))
        for channel_index, channel_key in enumerate(STANDARD_SHEETS, start=1):
            channel_observations.append(
                ChannelObservation(
                    business_date,
                    daily_version_id,
                    channel_key,
                    channel_index,
                    rows_per_sheet,
                )
            )
            for row_index in range(rows_per_sheet):
                nights = 1 + (row_index % 3)
                no_of_rooms = 1 + (row_index % 2)
                real_price = Decimal("321.17") + Decimal(row_index % 19) / Decimal("100")
                sequence = record_id
                facts.append(
                    MonthlyFact(
                        daily_record_id=sequence,
                        daily_version_id=daily_version_id,
                        business_date=business_date,
                        channel_key=channel_key,
                        arrival=business_date,
                        departure=business_date + timedelta(days=nights),
                        nights=nights,
                        adults=2,
                        children=row_index % 2,
                        block_code=f"BLOCK-{sequence}",
                        no_of_rooms=no_of_rooms,
                        company_name=f"COMPANY-{channel_index}",
                        confirmation_no=f"CONF-{sequence}",
                        disp_room_no=f"ROOM-{sequence}",
                        effective_rate_amount=Decimal("247.35")
                        + Decimal((row_index + channel_index) % 7) / Decimal("100"),
                        full_name=f"GUEST-{sequence}",
                        res_comment=f"COMMENT-{sequence}",
                        trace_text="TRACE",
                        products="ROOM",
                        rate_code="BAR",
                        room_category_label="STD",
                        real_price=real_price,
                        total_price=real_price * no_of_rooms * nights,
                        kb_amount=Decimal(no_of_rooms * 100)
                        if channel_key == "DY-AI-Easy-KB"
                        else None,
                    )
                )
                record_id += 1
    return build_monthly_report(
        2026,
        8,
        date(2026, 8, 3),
        MonthlySnapshot(
            tuple(facts),
            tuple(daily_versions),
            tuple(channel_observations),
        ),
    )


class MonthlyReportsXlsxTests(unittest.TestCase):
    def test_openpyxl_builder_reopens_checks_formulas_and_semantic_identity(self):
        report = report_fixture()
        with tempfile.TemporaryDirectory(prefix="monthly-report-xlsx-") as temp_dir:
            built = OpenpyxlWorkbookBuilder().build(report, Path(temp_dir))

            self.assertTrue(built.path.is_file())
            self.assertEqual(built.path.stat().st_mode & 0o777, 0o600)
            self.assertEqual(built.summary["sheet_names"][:5], [
                "LIANTAI-GROUP", "LIANTAI-FIT", "QBD", "DY-AI-Easy-KB", "FENGRUN"
            ])
            self.assertEqual(built.summary["row_counts"], [0, 0, 1, 0, 0])
            self.assertEqual(built.summary["formula_count"], 1)
            self.assertEqual(built.summary["preview_count"], 5)
            self.assertEqual(len(built.summary["semantic_sha256"]), 64)

            workbook = load_workbook(built.path, data_only=False, read_only=True)
            try:
                self.assertEqual(workbook["QBD"]["S2"].value, "=R2*C2*G2")
                self.assertEqual(workbook["QBD"]["F2"].value, "'=SYN-FORMULA-LIKE")
                formulas = [
                    cell.value
                    for sheet in workbook.worksheets
                    for row in sheet.iter_rows()
                    for cell in row
                    if cell.data_type == "f"
                ]
                self.assertEqual(formulas, ["=R2*C2*G2"])
            finally:
                workbook.close()

    def test_openpyxl_builder_validates_440_decimal_rows_across_multiple_sheets(self):
        report = large_decimal_report_fixture()
        self.assertEqual(report.row_count, 440)

        with tempfile.TemporaryDirectory(prefix="monthly-report-xlsx-large-") as temp_dir:
            built = OpenpyxlWorkbookBuilder().build(report, Path(temp_dir))

            self.assertEqual(built.summary["sheet_names"], list(STANDARD_SHEETS))
            self.assertEqual(built.summary["row_counts"], [88, 88, 88, 88, 88])
            self.assertEqual(built.summary["formula_count"], 440)
            self.assertTrue(
                OpenpyxlWorkbookBuilder._decimal_matches("321.1700005", "321.17")
            )
            self.assertFalse(
                OpenpyxlWorkbookBuilder._decimal_matches("321.1700011", "321.17")
            )
            self.assertEqual(
                OpenpyxlWorkbookBuilder._validate_workbook(
                    built.path,
                    report,
                    report.to_workbook_payload(),
                ),
                440,
            )


if __name__ == "__main__":
    unittest.main()
