from __future__ import annotations

import json
import os
import shutil
import subprocess
import tempfile
import unittest
from datetime import date
from decimal import Decimal
from pathlib import Path

from openpyxl import load_workbook

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


PROJECT_ROOT = Path(__file__).resolve().parents[1]
BUILDER = PROJECT_ROOT / "monthly_reports" / "xlsx" / "build_workbook.mjs"


def artifact_tool_module() -> str:
    return os.environ.get("MONTHLY_REPORT_ARTIFACT_TOOL_MODULE", "").strip()


def node_binary() -> str:
    return os.environ.get("MONTHLY_REPORT_NODE_BINARY", "").strip() or shutil.which("node") or ""


def report_payload() -> dict:
    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,
    )
    report = 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),),
        ),
    )
    return report.to_workbook_payload()


@unittest.skipUnless(
    bool(artifact_tool_module() and Path(artifact_tool_module()).is_file()),
    "artifact-tool module is not configured",
)
class MonthlyReportsXlsxTests(unittest.TestCase):
    def test_builder_exports_reopens_scans_and_renders_every_channel(self):
        node = node_binary()
        if not node:
            self.skipTest("Node.js is unavailable")
        with tempfile.TemporaryDirectory(prefix="monthly-report-xlsx-") as temp_dir:
            root = Path(temp_dir)
            payload_path = root / "payload.json"
            payload_path.write_text(
                json.dumps(report_payload(), ensure_ascii=False),
                encoding="utf-8",
            )
            output = root / "monthly.xlsx"
            previews = root / "previews"
            summary_path = root / "summary.json"
            completed = subprocess.run(
                [
                    node,
                    str(BUILDER),
                    str(payload_path),
                    str(output),
                    str(previews),
                    str(summary_path),
                ],
                cwd=PROJECT_ROOT,
                capture_output=True,
                text=True,
                timeout=120,
                check=False,
                env=os.environ.copy(),
            )

            self.assertEqual(completed.returncode, 0, completed.stderr)
            summary = json.loads(summary_path.read_text(encoding="utf-8"))
            self.assertEqual(summary["sheet_names"][:5], [
                "LIANTAI-GROUP", "LIANTAI-FIT", "QBD", "DY-AI-Easy-KB", "FENGRUN"
            ])
            self.assertEqual(summary["row_counts"], [0, 0, 1, 0, 0])
            self.assertEqual(summary["formula_count"], 1)
            self.assertEqual(summary["preview_count"], 5)
            self.assertEqual(len(list(previews.glob("sheet-*.png"))), 5)
            self.assertEqual(output.stat().st_mode & 0o777, 0o600)
            self.assertFalse(Path(f"{output}.inspect.ndjson").exists())
            workbook = load_workbook(output, data_only=False, read_only=True)
            try:
                self.assertEqual(workbook["QBD"]["S2"].value, "=R2*C2*G2")
                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_invalid_payload_returns_only_stable_error_code(self):
        node = node_binary()
        if not node:
            self.skipTest("Node.js is unavailable")
        with tempfile.TemporaryDirectory(prefix="monthly-report-xlsx-") as temp_dir:
            root = Path(temp_dir)
            invalid = root / "invalid.json"
            invalid.write_text(
                json.dumps({"schema_version": "bad", "sensitive": "SYN-SECRET"}),
                encoding="utf-8",
            )
            completed = subprocess.run(
                [
                    node,
                    str(BUILDER),
                    str(invalid),
                    str(root / "invalid.xlsx"),
                    str(root / "previews"),
                    str(root / "summary.json"),
                ],
                cwd=PROJECT_ROOT,
                capture_output=True,
                text=True,
                timeout=30,
                check=False,
                env=os.environ.copy(),
            )
            self.assertEqual(completed.returncode, 4)
            self.assertEqual(
                json.loads(completed.stderr),
                {
                    "status": "failed",
                    "code": "MONTHLY_REPORT_OUTPUT_VALIDATION_FAILED",
                },
            )
            self.assertNotIn("SYN-SECRET", completed.stderr)


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