from __future__ import annotations

import json
import tempfile
import unittest
from pathlib import Path

from us_cpa.questions import QuestionEngine, render_analysis, render_memo
from us_cpa.sources import TaxYearCorpus, bootstrap_irs_catalog


class QuestionEngineTests(unittest.TestCase):
    def build_engine(self, temp_dir: str) -> QuestionEngine:
        corpus = TaxYearCorpus(cache_root=Path(temp_dir))

        def fake_fetch(url: str) -> bytes:
            if "p501" in url:
                return (
                    "A qualifying child may be your dependent if the relationship, age, residency, support, and joint return tests are met. "
                    "Temporary absences due to education count as time lived with you. "
                    "To meet the support test, the child must not have provided more than half of their own support for the year."
                ).encode()
            return f"source for {url}".encode()

        corpus.download_catalog(2025, bootstrap_irs_catalog(2025), fetcher=fake_fetch)
        return QuestionEngine(corpus=corpus)

    def test_standard_deduction_question_returns_structured_analysis(self) -> None:
        with tempfile.TemporaryDirectory() as temp_dir:
            engine = self.build_engine(temp_dir)

            analysis = engine.answer(
                question="What is the standard deduction for single filers?",
                tax_year=2025,
                case_facts={"filingStatus": "single"},
            )

            self.assertEqual(analysis["issue"], "standard_deduction")
            self.assertEqual(analysis["taxYear"], 2025)
            self.assertEqual(analysis["conclusion"]["answer"], "$15,750")
            self.assertEqual(analysis["confidence"], "high")
            self.assertEqual(analysis["riskLevel"], "low")
            self.assertTrue(analysis["authorities"])
            self.assertEqual(analysis["authorities"][0]["sourceClass"], "irs_instructions")

    def test_standard_deduction_infers_married_filing_jointly_from_question(self) -> None:
        with tempfile.TemporaryDirectory() as temp_dir:
            engine = self.build_engine(temp_dir)

            analysis = engine.answer(
                question="What is the standard deduction for married filing jointly?",
                tax_year=2025,
                case_facts={},
            )

            self.assertEqual(analysis["issue"], "standard_deduction")
            self.assertEqual(analysis["conclusion"]["answer"], "$31,500")
            self.assertIn("Married Filing Jointly", analysis["conclusion"]["summary"])

    def test_standard_deduction_infers_head_of_household_from_question(self) -> None:
        with tempfile.TemporaryDirectory() as temp_dir:
            engine = self.build_engine(temp_dir)

            analysis = engine.answer(
                question="What is the standard deduction for a head of household filer?",
                tax_year=2025,
                case_facts={},
            )

            self.assertEqual(analysis["issue"], "standard_deduction")
            self.assertEqual(analysis["conclusion"]["answer"], "$23,625")
            self.assertIn("Head Of Household", analysis["conclusion"]["summary"])

    def test_standard_deduction_infers_qualifying_surviving_spouse_from_question(self) -> None:
        with tempfile.TemporaryDirectory() as temp_dir:
            engine = self.build_engine(temp_dir)

            analysis = engine.answer(
                question="What is the standard deduction for a qualifying surviving spouse?",
                tax_year=2025,
                case_facts={},
            )

            self.assertEqual(analysis["issue"], "standard_deduction")
            self.assertEqual(analysis["conclusion"]["answer"], "$31,500")
            self.assertIn("Qualifying Surviving Spouse", analysis["conclusion"]["summary"])

    def test_standard_deduction_infers_qualifying_widow_from_question(self) -> None:
        with tempfile.TemporaryDirectory() as temp_dir:
            engine = self.build_engine(temp_dir)

            analysis = engine.answer(
                question="What is the standard deduction for a qualifying widow?",
                tax_year=2025,
                case_facts={},
            )

            self.assertEqual(analysis["issue"], "standard_deduction")
            self.assertEqual(analysis["conclusion"]["answer"], "$31,500")
            self.assertIn("Qualifying Surviving Spouse", analysis["conclusion"]["summary"])

    def test_dependency_question_uses_irs_corpus_research_before_primary_law(self) -> None:
        with tempfile.TemporaryDirectory() as temp_dir:
            engine = self.build_engine(temp_dir)

            analysis = engine.answer(
                question=(
                    "If my daughter went to college in 2025 starting in August, but also worked before that, "
                    "should she be considered as a dependent?"
                ),
                tax_year=2025,
                case_facts={},
            )

            self.assertEqual(analysis["issue"], "irs_corpus_research")
            self.assertFalse(analysis["primaryLawRequired"])
            self.assertEqual(analysis["authorities"][0]["slug"], "p501")
            self.assertTrue(any(item["slug"] == "p501" for item in analysis["authorities"]))
            self.assertTrue(analysis["excerpts"])

    def test_complex_question_flags_primary_law_escalation(self) -> None:
        with tempfile.TemporaryDirectory() as temp_dir:
            engine = self.build_engine(temp_dir)

            analysis = engine.answer(
                question="Does section 469 passive activity loss limitation apply here?",
                tax_year=2025,
                case_facts={},
            )

            self.assertEqual(analysis["confidence"], "low")
            self.assertEqual(analysis["riskLevel"], "high")
            self.assertTrue(analysis["primaryLawRequired"])
            self.assertIn("Internal Revenue Code", analysis["missingFacts"][0])
            self.assertTrue(any(item["sourceClass"] == "internal_revenue_code" for item in analysis["authorities"]))

    def test_capital_gains_question_returns_schedule_d_guidance(self) -> None:
        with tempfile.TemporaryDirectory() as temp_dir:
            engine = self.build_engine(temp_dir)

            analysis = engine.answer(
                question="Do I need Schedule D for capital gains?",
                tax_year=2025,
                case_facts={"capitalGainLoss": 400},
            )

            self.assertEqual(analysis["issue"], "schedule_d_required")
            self.assertEqual(analysis["confidence"], "medium")
            self.assertFalse(analysis["primaryLawRequired"])
            self.assertTrue(any(item["slug"] == "f1040sd" for item in analysis["authorities"]))

    def test_schedule_e_question_returns_rental_guidance(self) -> None:
        with tempfile.TemporaryDirectory() as temp_dir:
            engine = self.build_engine(temp_dir)

            analysis = engine.answer(
                question="Do I need Schedule E for rental income?",
                tax_year=2025,
                case_facts={"rentalIncome": 1200},
            )

            self.assertEqual(analysis["issue"], "schedule_e_required")
            self.assertFalse(analysis["primaryLawRequired"])
            self.assertTrue(any(item["slug"] == "f1040se" for item in analysis["authorities"]))

    def test_renderers_produce_conversation_and_memo(self) -> None:
        analysis = {
            "issue": "standard_deduction",
            "taxYear": 2025,
            "factsUsed": [{"field": "filingStatus", "value": "single"}],
            "missingFacts": [],
            "authorities": [{"title": "Instructions for Form 1040 and Schedules 1-3"}],
            "conclusion": {"answer": "$15,750", "summary": "Single filers use a $15,750 standard deduction for tax year 2025."},
            "confidence": "high",
            "riskLevel": "low",
            "followUpQuestions": [],
            "primaryLawRequired": False,
        }

        conversation = render_analysis(analysis)
        memo = render_memo(analysis)

        self.assertIn("$15,750", conversation)
        self.assertIn("Issue", memo)
        self.assertIn("Authorities", memo)


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