"""The dedicated artifact carries the complete closed result independently of log masking."""
from contextlib import redirect_stdout
import hashlib
import io
import json
from pathlib import Path
import sys
import tempfile
import unittest
from unittest.mock import patch
from test_identity_diagnostics import READER, SINCE


class IdentityArtifact(unittest.TestCase):
    def invoke(self, path, *, result=None, error=None, remote=False):
        argv = ['reader', '--app', 'fixture', '--service', 'api', '--since', SINCE, '--output', str(path)]
        if remote:
            argv.append('--remote')
        output = io.StringIO()
        with patch.object(sys, 'argv', argv), patch.object(READER, 'local', return_value=result,
                side_effect=error), redirect_stdout(output):
            status = READER.main()
        return status, output.getvalue()

    def test_success_file_survives_structural_log_redaction_byte_for_byte(self):
        result = {'schema': READER.SCHEMA, 'ok': True, 'failure': '',
                  'records': [{'stage': 'auth_signature', 'reason': 'token_invalid'}]}
        with tempfile.TemporaryDirectory() as folder:
            path = Path(folder) / 'result.json'
            status, log = self.invoke(path, result=result)
            data = path.read_bytes()
            self.assertEqual(status, 0)
            self.assertEqual(json.loads(data), result)
            self.assertNotEqual(log.replace('{', '***').replace('}', '***'), data.decode())
            self.assertEqual(hashlib.sha256(data).digest(), hashlib.sha256(log.encode()).digest())

    def test_classified_and_unknown_failures_are_retained_without_exception_data(self):
        for failure, expected in [(READER.ReaderFailure('container_scope_mismatch'), 'container_scope_mismatch'),
                                  (RuntimeError('PRIVATE_SENTINEL'), 'unclassified')]:
            with self.subTest(expected=expected), tempfile.TemporaryDirectory() as folder:
                path = Path(folder) / 'result.json'
                status, log = self.invoke(path, error=failure)
                self.assertEqual(status, 1)
                self.assertEqual(json.loads(path.read_bytes()), {
                    'schema': READER.SCHEMA, 'ok': False, 'failure': expected, 'records': []})
                self.assertNotIn('PRIVATE_SENTINEL', log + path.read_text())

    def test_unvalidated_fields_never_reach_artifact(self):
        invalid = {'schema': READER.SCHEMA, 'ok': True, 'failure': '', 'records': [], 'raw': 'PRIVATE_SENTINEL'}
        with tempfile.TemporaryDirectory() as folder:
            path = Path(folder) / 'result.json'
            status, log = self.invoke(path, result=invalid)
            self.assertEqual(status, 1)
            self.assertEqual(json.loads(path.read_text())['failure'], 'remote_invalid')
            self.assertNotIn('PRIVATE_SENTINEL', log + path.read_text())

    def test_existing_file_is_preserved_and_write_failure_is_closed(self):
        with tempfile.TemporaryDirectory() as folder:
            path = Path(folder) / 'result.json'; path.write_text('existing')
            status, log = self.invoke(path, error=READER.ReaderFailure('invalid_scope'))
            self.assertEqual(status, 1)
            self.assertEqual(json.loads(log)['failure'], 'artifact_write_failed')
            self.assertEqual(path.read_text(), 'existing')

    def test_remote_mode_cannot_create_output_files(self):
        with tempfile.TemporaryDirectory() as folder:
            path = Path(folder) / 'result.json'
            with patch.object(READER, 'remote') as remote:
                status, log = self.invoke(path, remote=True)
            self.assertEqual(status, 1)
            self.assertEqual(json.loads(log)['failure'], 'invalid_destination')
            self.assertFalse(path.exists())
            remote.assert_not_called()


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