"""Classify refused policy shapes without publishing their private values or relaxing admission."""
import copy
import json
import os
import unittest
from unittest.mock import patch

from network_diagnostics import SCHEMA, decode
from network_inventory import collect
from network_pools import InventoryFailure, validate_rules
from test_network_inventory import FakeHost, RULES


class RuleDiagnosticTests(unittest.TestCase):
    def test_iproute_decimal_table_strings_keep_exact_policy_semantics(self):
        marked = {'src': 'all', 'fwmark': '0x80000', 'fwmask': '0xff0000'}
        rules = [dict(row, table=str({'local': 255, 'main': 254, 'default': 253}[row['table']]))
                 for row in RULES]
        validate_rules(rules)
        rules += [dict(marked, priority=5210, table='254'), dict(marked, priority=5230, table='253'),
                  dict(marked, priority=5250, action='unreachable'),
                  {'priority': 5270, 'src': 'all', 'table': '52'}]
        validate_rules(rules)
        for invalid in ('53', '052', '0x34', 'private table alias', 53, True):
            changed = copy.deepcopy(rules)
            changed[-1]['table'] = invalid
            with self.subTest(table=invalid), self.assertRaises(InventoryFailure) as caught:
                validate_rules(changed)
            self.assertEqual(caught.exception.code, 'host_rules_unsupported_table')

    def test_each_rejected_shape_keeps_a_closed_reason_without_values(self):
        marked = {'src': 'all', 'fwmark': '0x80000', 'fwmask': '0xff0000'}
        valid = RULES + [dict(marked, priority=5210, table='main'), dict(marked, priority=5230, table='default'),
                         dict(marked, priority=5250, action='unreachable'),
                         {'priority': 5270, 'src': 'all', 'table': 52}]
        validate_rules(valid)
        cases = [('fields', 'PRIVATE_SENTINEL', 'PRIVATE_SENTINEL'), ('priority', 'priority', True),
                 ('source', 'src', 'PRIVATE_SENTINEL'), ('table', 'table', 'PRIVATE_SENTINEL'),
                 ('fwmark', 'fwmark', 'PRIVATE_SENTINEL'), ('fwmask', 'fwmask', 'PRIVATE_SENTINEL')]
        for reason, key, value in cases:
            rows = copy.deepcopy(valid)
            rows[3][key] = value
            with self.subTest(reason=reason), self.assertRaises(InventoryFailure) as caught:
                validate_rules(rows)
            self.assertEqual(caught.exception.code, 'host_rules_unsupported_' + reason)
            self.assertNotIn('PRIVATE_SENTINEL', str(caught.exception))
        for rows, reason in ((valid[:-1], 'count'), (valid[:-1] + [valid[0]], 'duplicate'),
                             ([dict(row, fwmark='0x80000') for row in RULES], 'structure')):
            with self.subTest(reason=reason), self.assertRaises(InventoryFailure) as caught:
                validate_rules(rows)
            self.assertEqual(caught.exception.code, 'host_rules_unsupported_' + reason)

    def test_scoped_diagnostic_retains_reason_and_partial_metadata_only(self):
        host = FakeHost()
        host.answers[('ip', '-j', '-4', 'rule', 'show')][0]['src'] = 'PRIVATE_SENTINEL'
        with patch.dict(os.environ, {}, clear=True), self.assertRaises(InventoryFailure) as caught:
            collect(host, require_pools=False)
        failure = caught.exception
        result = dict(failure.details, schema=SCHEMA, ok=False, project='fixture', failure=failure.code)
        raw = json.dumps(result).encode()
        self.assertEqual(decode(1, raw, 'fixture')['failure'], 'host_rules_unsupported_source')
        self.assertFalse(result['complete'])
        self.assertNotIn(b'PRIVATE_SENTINEL', raw)
        self.assertNotIn('networks', result)
        self.assertFalse(any('create' in call or 'rm' in call for call in host.calls))


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