import json
import subprocess
import sys
import unittest
from unittest.mock import patch

from identity_diagnostics import ReaderFailure
from network_diagnostics import SCHEMA, decode, diagnose, remote_source
from network_test_fixture import Host
from test_network_selected import fixture


class DiagnosticTests(unittest.TestCase):
    def test_streamed_package_import_graph_runs_without_remote_files(self):
        inventory = Host().inventory(None)
        inventory['pool_source'] = 'engine_info'
        source = remote_source('unrelated').decode().replace(
            'inventory = collect(run, require_pools=False)', 'inventory = ' + repr(inventory))
        result = subprocess.run([sys.executable, '-'], input=source.encode(), stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE, timeout=5)
        self.assertEqual(result.returncode, 0, result.stderr.decode())
        evidence = decode(0, result.stdout, 'unrelated')
        self.assertEqual(evidence['network_count'], 1)
        self.assertEqual(evidence['networks'][0]['name'], 'unrelated_default')
        self.assertNotIn('Labels', result.stdout.decode())
        self.assertNotIn('Containers', result.stdout.decode())

    def test_diagnostic_uses_only_stdin_package_and_existing_deployment_identity(self):
        evidence = {'schema': SCHEMA, 'ok': False, 'project': 'fixture', 'failure': 'host_rules_unsupported'}
        with patch('network_diagnostics.bounded', return_value=(1, json.dumps(evidence).encode())) as execute:
            self.assertEqual(diagnose('fixture', 'host.example', 'root'), evidence)
        args, kwargs = execute.call_args
        self.assertEqual(args[0][-1], "python3 -")
        self.assertIn('root@host.example', args[0])
        self.assertNotIn('rsync', args[0])
        self.assertIn(b'network_inventory', kwargs['payload'])
        self.assertNotIn(b'network_prepare', kwargs['payload'])

    def test_partial_metadata_survives_routing_refusal(self):
        evidence = {'schema': SCHEMA, 'ok': False, 'project': 'fixture', 'failure': 'host_rules_unsupported',
                    'docker_version': '28.0.4+vendor.1', 'compose_version': '2.39.4',
                    'python_version': '3.8.20', 'pools': [],
                    'pool_source': 'unverified', 'pool_failure': 'pool_provenance_unknown',
                    'observed_at': '2026-09-15T00:00:00+00:00', 'complete': False}
        self.assertEqual(decode(1, json.dumps(evidence).encode(), 'fixture'), evidence)

    def test_optional_metadata_failure_keeps_independent_selected_network_observation(self):
        inventory, selected = fixture()
        metadata = dict.fromkeys(('droplet_id', 'region', 'public_ipv4', 'private_ipv4',
                                  'private_subnet', 'private_gateway'), '')
        metadata.update(provider='digitalocean', status='unavailable', reason='metadata_timeout',
                        private_gateway_route='unverified')
        source = remote_source('fixture', '138.197.36.107', 'digitalocean').decode().replace(
            'inventory = collect(run, require_pools=False)', 'inventory = ' + repr(inventory)).replace(
            "observe(inventory, '138.197.36.107')", repr(metadata))
        result = subprocess.run([sys.executable, '-'], input=source.encode(), stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE, timeout=5)
        self.assertEqual(result.returncode, 0, result.stderr.decode())
        evidence = decode(0, result.stdout, 'fixture')
        self.assertEqual(evidence['host_identity'], metadata)
        self.assertEqual(evidence['networks'][0]['validation']['overlap'], 'clear')
        evidence['host_identity']['secret'] = 'PRIVATE_SENTINEL'
        with self.assertRaises(ReaderFailure):
            decode(0, json.dumps(evidence).encode(), 'fixture')

    def test_unrecognized_fields_codes_and_transport_results_are_not_published(self):
        evidence = {'schema': SCHEMA, 'ok': False, 'project': 'fixture', 'failure': 'host_rules_unsupported'}
        for changed in ({**evidence, 'secret': 'PRIVATE_SENTINEL'}, {**evidence, 'failure': 'PRIVATE_SENTINEL'},
                        {**evidence, 'project': 'another'}, {**evidence, 'pools': [{'secret': 'PRIVATE_SENTINEL'}]}):
            with self.assertRaises(ReaderFailure):
                decode(1, json.dumps(changed).encode(), 'fixture')
        with self.assertRaises(ReaderFailure):
            decode(255, json.dumps(evidence).encode(), 'fixture')


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