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

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

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import corporate_engines as ce  # noqa: E402


class RightsIssueTests(unittest.TestCase):
    def test_standard_1_for_4(self):
        out = ce.rights_issue({"existing_shares": 100000, "ratio_new": 1, "ratio_held": 4,
                               "subscription_price": 150, "cum_rights_price": 250})
        self.assertEqual(out["new_shares"], 25000.0)
        self.assertEqual(out["total_shares_after"], 125000.0)
        self.assertEqual(out["proceeds"], 3750000.0)
        self.assertEqual(out["theoretical_ex_rights_price"], 230.0)
        self.assertEqual(out["value_of_right"], 20.0)

    def test_zero_ratio_held_does_not_crash(self):
        out = ce.rights_issue({"existing_shares": 1000, "ratio_new": 1, "ratio_held": 0})
        self.assertEqual(out["new_shares"], 0.0)

    def test_no_existing_shares_zero_terp(self):
        out = ce.rights_issue({"existing_shares": 0, "ratio_new": 1, "ratio_held": 4, "cum_rights_price": 100})
        self.assertEqual(out["theoretical_ex_rights_price"], 0.0)


class BonusIssueTests(unittest.TestCase):
    def test_reserves_sufficient(self):
        out = ce.bonus_issue({"existing_shares": 100000, "ratio_new": 1, "ratio_held": 5,
                              "face_value": 100, "reserves_available": 2500000})
        self.assertEqual(out["bonus_shares"], 20000.0)
        self.assertEqual(out["capitalized_amount"], 2000000.0)
        self.assertTrue(out["reserves_sufficient"])
        self.assertEqual(out["reserves_shortfall"], 0.0)

    def test_reserves_insufficient(self):
        out = ce.bonus_issue({"existing_shares": 100000, "ratio_new": 1, "ratio_held": 5,
                              "face_value": 100, "reserves_available": 1800000})
        self.assertFalse(out["reserves_sufficient"])
        self.assertEqual(out["reserves_shortfall"], 200000.0)


class CapitalReductionTests(unittest.TestCase):
    def test_ratio_mode(self):
        out = ce.capital_reduction({"existing_capital": 10000000, "existing_shares": 100000,
                                    "face_value": 100, "mode": "ratio", "reduction_pct": 30})
        self.assertEqual(out["reduction_amount"], 3000000.0)
        self.assertEqual(out["new_capital"], 7000000.0)
        self.assertEqual(out["new_shares"], 70000.0)
        self.assertEqual(out["shares_cancelled"], 30000.0)

    def test_amount_mode_derives_pct(self):
        out = ce.capital_reduction({"existing_capital": 10000000, "existing_shares": 100000,
                                    "face_value": 100, "mode": "amount", "reduction_amount": 2000000})
        self.assertEqual(out["reduction_pct"], 20.0)
        self.assertEqual(out["new_capital"], 8000000.0)

    def test_reduction_cannot_go_negative(self):
        out = ce.capital_reduction({"existing_capital": 1000000, "existing_shares": 10000,
                                    "face_value": 100, "mode": "amount", "reduction_amount": 5000000})
        self.assertEqual(out["new_capital"], 0.0)
        self.assertGreaterEqual(out["shares_cancelled"], 0.0)


class DividendDistributionTests(unittest.TestCase):
    def test_sufficient_distribution(self):
        out = ce.dividend_distribution_check({"net_profit_current_year": 5000000, "accumulated_losses_bf": 0,
                                              "free_reserves": 0, "mandatory_reserve_pct": 20,
                                              "proposed_dividend_amount": 3000000})
        self.assertTrue(out["sufficient"])
        self.assertEqual(out["shortfall"], 0.0)

    def test_insufficient_distribution_flags(self):
        out = ce.dividend_distribution_check({"net_profit_current_year": 5000000, "accumulated_losses_bf": 500000,
                                              "free_reserves": 200000, "mandatory_reserve_pct": 20,
                                              "proposed_dividend_amount": 4000000})
        self.assertFalse(out["sufficient"])
        self.assertEqual(out["shortfall"], 200000.0)
        self.assertTrue(any(f["severity"] == "High" for f in out["flags"]))

    def test_losses_exceed_profit_flags_high(self):
        out = ce.dividend_distribution_check({"net_profit_current_year": 300000, "accumulated_losses_bf": 500000,
                                              "proposed_dividend_amount": 0})
        self.assertTrue(any("accumulated losses" in f["message"] for f in out["flags"]))

    def test_no_reserve_pct_flags_low(self):
        out = ce.dividend_distribution_check({"net_profit_current_year": 1000000, "proposed_dividend_amount": 100000})
        self.assertTrue(any(f["severity"] == "Low" for f in out["flags"]))


class ShareholdingAnalysisTests(unittest.TestCase):
    def test_ownership_and_concentration(self):
        out = ce.shareholding_analysis([
            {"id": 1, "name": "A", "shares_held": 40000, "is_related_party": True, "is_director": True},
            {"id": 2, "name": "B", "shares_held": 30000},
            {"id": 3, "name": "C", "shares_held": 30000, "share_class": "preference"},
        ])
        self.assertEqual(out["total_shares"], 100000.0)
        self.assertEqual(out["top5_concentration_pct"], 100.0)
        self.assertEqual(out["related_party_pct"], 40.0)
        self.assertEqual(out["shares_by_class"]["preference"], 30000.0)

    def test_empty_register_no_crash(self):
        out = ce.shareholding_analysis([])
        self.assertEqual(out["total_shares"], 0.0)
        self.assertEqual(out["holder_count"], 0)


class BoardComplianceTests(unittest.TestCase):
    def test_gap_breach_detected(self):
        out = ce.board_compliance([
            {"meeting_type": "board", "meeting_date": "2025-01-10", "minutes_recorded": True, "status": "held"},
            {"meeting_type": "board", "meeting_date": "2025-07-15", "minutes_recorded": False, "status": "held"},
        ], max_gap_days=120)
        self.assertEqual(out["gap_breaches_count"], 1)
        self.assertEqual(out["minutes_filed_pct"], 50.0)

    def test_cancelled_meetings_excluded_from_cadence(self):
        out = ce.board_compliance([
            {"meeting_type": "board", "meeting_date": "2025-01-10", "status": "cancelled"},
        ], max_gap_days=120)
        self.assertEqual(out["board_meetings_count"], 0)

    def test_no_max_gap_no_breaches(self):
        out = ce.board_compliance([
            {"meeting_type": "board", "meeting_date": "2025-01-10", "status": "held"},
            {"meeting_type": "board", "meeting_date": "2025-12-10", "status": "held"},
        ])
        self.assertEqual(out["gap_breaches_count"], 0)


class ShareTransactionComplianceTests(unittest.TestCase):
    def test_agm_required_event_missing_dates(self):
        out = ce.share_transaction_compliance([
            {"id": 1, "event_type": "rights_issue", "status": "approved", "board_approval_date": "2025-01-01"},
        ])
        self.assertFalse(out["rows"][0]["complete"])
        self.assertIn("AGM/EGM (shareholder) approval date", out["rows"][0]["missing"])

    def test_transfer_does_not_need_agm(self):
        out = ce.share_transaction_compliance([
            {"id": 2, "event_type": "share_transfer", "status": "completed",
             "board_approval_date": "2025-02-01", "ocr_filed_date": "2025-02-15"},
        ])
        self.assertTrue(out["rows"][0]["complete"])
        self.assertFalse(out["rows"][0]["needs_agm_approval"])

    def test_empty_register(self):
        out = ce.share_transaction_compliance([])
        self.assertEqual(out["total"], 0)
        self.assertEqual(out["complete_pct"], 0.0)


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