"""Synthetic arithmetic/counterexample checks, not sensor or recovery validation."""
import copy
import json
from pathlib import Path
import unittest
from material_balance import reconcile, FIELDS

BASE = dict(unit='g', opening_stock_g=120000, incoming_g=1000000, other_inflow_g=0,
            material_output_g=620000, residue_output_g=230000, other_outflow_g=0,
            closing_stock_g=270000)


class MaterialBalanceTests(unittest.TestCase):
    def test_fixture_cases(self):
        f = json.loads(Path(__file__).with_name('synthetic-material-accounts.json').read_text())
        self.assertEqual(len({r['id'] for r in f['cases']}), 4)
        for r in f['cases']:
            with self.subTest(case=r['id']):
                x = reconcile(r['account'], f['tolerance_g'])
                self.assertEqual(x['status'], r['expected_status'])
                self.assertEqual(x['discrepancy_g'], r['expected_discrepancy_g'])

    def test_inventory_terms_are_included(self):
        r = reconcile(BASE, 0)
        self.assertEqual((r['available_g'],r['accounted_g']), (1120000,1120000))
        self.assertEqual(reconcile({**BASE,'opening_stock_g':0},0)['discrepancy_g'],-120000)

    def test_other_inflows_and_outflows(self):
        self.assertEqual(reconcile({**BASE,'other_inflow_g':75,'other_outflow_g':75},0)['status'],'balanced')
        self.assertEqual(reconcile({**BASE,'other_inflow_g':75},0)['discrepancy_g'],75)

    def test_positive_and_negative_discrepancy(self):
        self.assertEqual(reconcile({**BASE,'closing_stock_g':260000},0)['discrepancy_g'],10000)
        self.assertEqual(reconcile({**BASE,'closing_stock_g':280000},0)['discrepancy_g'],-10000)

    def test_nonzero_tolerance_is_not_perfect_balance(self):
        r=reconcile({**BASE,'closing_stock_g':269500},1000)
        self.assertEqual((r['status'],r['discrepancy_g']),('within_tolerance',500))

    def test_tolerance_boundaries(self):
        for delta in [-1000,1000]:
            self.assertEqual(reconcile({**BASE,'closing_stock_g':270000+delta},1000)['status'],'within_tolerance')
        self.assertEqual(reconcile({**BASE,'closing_stock_g':271001},1000)['status'],'unreconciled')

    def test_unknown_each_quantity_prevents_numeric_conclusion(self):
        for field in FIELDS:
            with self.subTest(field=field):
                r=reconcile({**BASE,field:None},0)
                self.assertEqual(r['status'],'incomplete')
                self.assertIsNone(r['discrepancy_g'])
                self.assertEqual(r['missing_fields'],[field])

    def test_zero_is_known(self):
        r=reconcile({**{f:0 for f in FIELDS},'unit':'g'},0)
        self.assertEqual((r['status'],r['discrepancy_g']),('balanced',0))

    def test_invalid_quantities(self):
        for value in [True,1.0,'1',-1,float('nan'),float('inf')]:
            with self.subTest(value=value),self.assertRaises(ValueError):reconcile({**BASE,'incoming_g':value},0)

    def test_no_implicit_unit_conversion(self):
        for u in ['kg','tonnes',None]:
            with self.subTest(unit=u),self.assertRaises(ValueError):reconcile({**BASE,'unit':u},0)

    def test_missing_and_unexpected_fields_rejected(self):
        a=copy.deepcopy(BASE);del a['incoming_g']
        for value in [a,{**BASE,'adjustment_g':0},None,[]]:
            with self.subTest(value=value),self.assertRaises(ValueError):reconcile(value,0)

    def test_validate_known_values_even_when_incomplete(self):
        with self.assertRaises(ValueError):reconcile({**BASE,'closing_stock_g':None,'incoming_g':-1},0)

    def test_invalid_tolerances_and_no_input_mutation(self):
        for t in [-1,True,0.5,'0',None]:
            with self.subTest(t=t),self.assertRaises(ValueError):reconcile(BASE,t)
        a=copy.deepcopy(BASE);reconcile(a,0);self.assertEqual(a,BASE)

    def test_large_integer_precision_and_no_yield(self):
        a={**{f:0 for f in FIELDS},'unit':'g','incoming_g':10**400,'material_output_g':10**400-1}
        r=reconcile(a,0)
        self.assertEqual(r['discrepancy_g'],1)
        self.assertFalse(any('rate' in k or 'yield' in k or 'percent' in k for k in r))


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