"""
Unit tests for server/india_engines.py.

Run with: cd server && ../venv/bin/python3 -m unittest tests.test_india_engines -v
"""
import os
import sys
import tempfile
import unittest

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))


class IndiaEnginesTests(unittest.TestCase):
    def setUp(self):
        self._tmpdir = tempfile.mkdtemp()
        os.environ["CA_DATA_DIR"] = self._tmpdir
        for mod in ("db", "country_service", "india_engines"):
            sys.modules.pop(mod, None)
        import db as db_mod
        import country_service as cs_mod
        import india_engines as ie_mod
        global db, cs, ie
        db, cs, ie = db_mod, cs_mod, ie_mod
        db.init_db()
        self.fy = db.current_fy_label()

    # ------------------------------------------------------------ depreciation

    def test_full_rate_addition_used_180_plus_days_gets_the_full_rate(self):
        with cs.use_country("IN"):
            result = ie.india_depreciation(self.fy, [{
                "block": "plant_15", "opening_wdv": 0,
                "additions": [{"desc": "Machine A", "amount": 100000, "put_to_use_days": 200}],
            }])
        block = result["blocks"][0]
        self.assertEqual(block["additions"][0]["rate_eligibility"], "full")
        self.assertEqual(block["depreciation"], 15000.0)  # 100000 * 15%

    def test_addition_used_under_180_days_gets_half_rate(self):
        with cs.use_country("IN"):
            result = ie.india_depreciation(self.fy, [{
                "block": "plant_15", "opening_wdv": 0,
                "additions": [{"desc": "Machine B", "amount": 100000, "put_to_use_days": 90}],
            }])
        block = result["blocks"][0]
        self.assertEqual(block["additions"][0]["rate_eligibility"], "half (< 180 days)")
        self.assertEqual(block["depreciation"], 7500.0)  # 100000 * 15% / 2

    def test_mixed_full_and_half_rate_additions_in_the_same_block(self):
        with cs.use_country("IN"):
            result = ie.india_depreciation(self.fy, [{
                "block": "plant_15", "opening_wdv": 200000,
                "additions": [
                    {"desc": "Full", "amount": 100000, "put_to_use_days": 250},
                    {"desc": "Half", "amount": 50000, "put_to_use_days": 60},
                ],
            }])
        block = result["blocks"][0]
        # (200000 + 100000) * 15% + 50000 * 7.5% = 45000 + 3750
        self.assertEqual(block["depreciation"], 48750.0)

    def test_disposal_proceeds_exceeding_wdv_is_a_short_term_capital_gain_not_negative_depreciation(self):
        with cs.use_country("IN"):
            result = ie.india_depreciation(self.fy, [{
                "block": "computer_40", "opening_wdv": 10000,
                "additions": [], "disposal_proceeds": 15000,
            }])
        block = result["blocks"][0]
        self.assertEqual(block["depreciation"], 0.0)
        self.assertEqual(block["short_term_capital_gain"], 5000.0)
        self.assertEqual(block["closing_wdv"], 0.0)

    def test_unknown_block_defaults_to_zero_rate_not_a_crash(self):
        with cs.use_country("IN"):
            result = ie.india_depreciation(self.fy, [{"block": "not_a_real_block", "opening_wdv": 100000}])
        self.assertEqual(result["blocks"][0]["rate"], 0)

    # ------------------------------------------------------------------- GST

    def test_intrastate_supply_splits_evenly_into_cgst_and_sgst(self):
        with cs.use_country("IN"):
            result = ie.india_gst_return(self.fy, {
                "outward_supplies": [{"description": "Sale", "taxable_value": 100000,
                                      "gst_rate_slug": "18", "is_interstate": False}],
            })
        line = result["outward_supplies"][0]
        self.assertEqual(line["cgst"], 9000.0)
        self.assertEqual(line["sgst"], 9000.0)
        self.assertEqual(line["igst"], 0.0)

    def test_interstate_supply_charges_full_rate_as_igst(self):
        with cs.use_country("IN"):
            result = ie.india_gst_return(self.fy, {
                "outward_supplies": [{"description": "Sale", "taxable_value": 100000,
                                      "gst_rate_slug": "18", "is_interstate": True}],
            })
        line = result["outward_supplies"][0]
        self.assertEqual(line["igst"], 18000.0)
        self.assertEqual(line["cgst"], 0.0)

    def test_ineligible_itc_is_excluded_from_credit(self):
        with cs.use_country("IN"):
            result = ie.india_gst_return(self.fy, {
                "outward_supplies": [],
                "inward_supplies": [{"description": "Personal use", "taxable_value": 10000,
                                     "gst_rate_slug": "18", "is_interstate": False, "itc_eligible": False}],
            })
        self.assertEqual(result["input_tax_credit"]["cgst"], 0.0)
        self.assertEqual(result["input_tax_credit"]["sgst"], 0.0)

    def test_net_payable_is_output_tax_less_eligible_input_credit(self):
        with cs.use_country("IN"):
            result = ie.india_gst_return(self.fy, {
                "outward_supplies": [{"description": "Sale", "taxable_value": 100000,
                                      "gst_rate_slug": "18", "is_interstate": False}],
                "inward_supplies": [{"description": "Purchase", "taxable_value": 40000,
                                     "gst_rate_slug": "18", "is_interstate": False, "itc_eligible": True}],
            })
        # Output: 9000 CGST + 9000 SGST. ITC: 3600 CGST + 3600 SGST.
        self.assertEqual(result["net_payable"]["cgst"], 5400.0)
        self.assertEqual(result["net_payable"]["sgst"], 5400.0)
        self.assertEqual(result["total_payable"], 10800.0)

    def test_leftover_igst_credit_offsets_cgst_then_sgst(self):
        with cs.use_country("IN"):
            result = ie.india_gst_return(self.fy, {
                "outward_supplies": [{"description": "Local sale", "taxable_value": 10000,
                                      "gst_rate_slug": "18", "is_interstate": False}],
                "inward_supplies": [{"description": "Interstate purchase", "taxable_value": 100000,
                                     "gst_rate_slug": "18", "is_interstate": True, "itc_eligible": True}],
            })
        # Output CGST+SGST = 900+900. IGST credit = 18000, fully offsets both,
        # 16200 carried forward.
        self.assertEqual(result["net_payable"]["cgst"], 0.0)
        self.assertEqual(result["net_payable"]["sgst"], 0.0)
        self.assertEqual(result["igst_credit_carried_forward"], 16200.0)

    # -------------------------------------------------------- interest & fee

    def test_234a_interest_on_shortfall_for_months_late(self):
        with cs.use_country("IN"):
            result = ie.india_interest_penalty(self.fy, {
                "assessed_tax": 100000, "tax_already_paid": 80000, "months_late_filing": 3,
            })
        line = next(l for l in result["lines"] if "234A" in l["head"])
        self.assertEqual(line["amount"], 600.0)  # 20000 * 1% * 3

    def test_234a_not_charged_when_fully_paid(self):
        with cs.use_country("IN"):
            result = ie.india_interest_penalty(self.fy, {
                "assessed_tax": 100000, "tax_already_paid": 100000, "months_late_filing": 3,
            })
        self.assertFalse(any("234A" in l["head"] for l in result["lines"]))

    def test_234b_charged_when_advance_tax_below_90_pct(self):
        with cs.use_country("IN"):
            result = ie.india_interest_penalty(self.fy, {
                "assessed_tax": 100000, "advance_tax_paid_total": 80000, "months_since_year_end": 2,
            })
        line = next(l for l in result["lines"] if "234B" in l["head"])
        self.assertEqual(line["amount"], 400.0)  # (100000-80000) * 1% * 2

    def test_234b_not_charged_when_90_pct_threshold_met(self):
        with cs.use_country("IN"):
            result = ie.india_interest_penalty(self.fy, {
                "assessed_tax": 100000, "advance_tax_paid_total": 90000, "months_since_year_end": 2,
            })
        self.assertFalse(any("234B" in l["head"] for l in result["lines"]))

    def test_234c_charges_each_shortfall_checkpoint_independently(self):
        with cs.use_country("IN"):
            result = ie.india_interest_penalty(self.fy, {
                "assessed_tax": 100000,
                "advance_tax_paid_by_jun15": 0,       # required 15000, full shortfall
                "advance_tax_paid_by_sep15": 45000,   # required 45000, no shortfall
                "advance_tax_paid_by_dec15": 60000,   # required 75000, shortfall 15000
                "advance_tax_paid_by_mar15": 100000,  # required 100000, no shortfall
            })
        heads = [l["head"] for l in result["lines"] if "234C" in l["head"]]
        self.assertEqual(len(heads), 2)
        jun_line = next(l for l in result["lines"] if "Jun 15" in l["head"])
        self.assertEqual(jun_line["amount"], 450.0)  # 15000 * 1% * 3

    def test_234f_fee_tiered_by_total_income(self):
        with cs.use_country("IN"):
            low = ie.india_interest_penalty(self.fy, {"filed_after_due_date": True, "total_income": 300000})
            high = ie.india_interest_penalty(self.fy, {"filed_after_due_date": True, "total_income": 800000})
        self.assertEqual(next(l for l in low["lines"] if "234F" in l["head"])["amount"], 1000.0)
        self.assertEqual(next(l for l in high["lines"] if "234F" in l["head"])["amount"], 5000.0)

    def test_234f_not_charged_when_filed_on_time(self):
        with cs.use_country("IN"):
            result = ie.india_interest_penalty(self.fy, {"filed_after_due_date": False, "total_income": 800000})
        self.assertFalse(any("234F" in l["head"] for l in result["lines"]))

    def test_every_line_carries_unverified_codes(self):
        with cs.use_country("IN"):
            result = ie.india_interest_penalty(self.fy, {
                "assessed_tax": 100000, "tax_already_paid": 0, "months_late_filing": 1,
            })
        self.assertIn("penalty.s234_interest_rate_pct", result["unverified"])

    # ------------------------------------------------------ dispatch wiring

    def test_callable_through_the_shared_engines_registry(self):
        import engines
        with cs.use_country("IN"):
            result = engines.compute("india_depreciation", self.fy,
                                      {"blocks": [{"block": "computer_40", "opening_wdv": 100000}]})
        self.assertEqual(result["blocks"][0]["depreciation"], 40000.0)


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