"""tests/test_submission_readiness.py — REQ-220.

Covers: good contract passes; missing tags fail with MISSING_TAGS;
env-missing-mandatory fails with ENV_MISSING_MANDATORY:<env>:<field>;
agentic profile missing intent fails with AGENTIC_MISSING_INTENT;
missing appSource fails with MISSING_APP_SOURCE.
"""
import json
import os
import sys
import unittest

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from core.submission_readiness import check_readiness, ReadinessResult

GOOD_TAGS = {
    "nova:owner": "consumer-repo",
    "nova:contract": "uuid-1234",
    "nova:environment": "dev",
    "nova:cost-center": "nova-default",
    "nova:ref": "CHG0678912",
}


def _base(**overrides):
    submission = {
        "contractId": "uuid-1234",
        "id": "webapi",
        "name": "Customer Web API",
        "environment": "dev",
        "tags": dict(GOOD_TAGS),
        "policyPreconditions": {"public-ingress": False, "encryption_enabled": True},
        "profile": "developer",
        "appSource": {"repo": "consumer/repo", "ref": "main"},
        "infrastructure": {
            "static-assets": {"inputs": {"bucket_name": "webapi-assets"}}
        },
    }
    submission.update(overrides)
    return submission


class TestGoodContract(unittest.TestCase):
    def test_good_contract_passes(self):
        result = check_readiness(_base())
        self.assertTrue(result.ready, f"Expected ready, got: {result.reason_codes}")
        self.assertEqual(result.contract_id, "uuid-1234")

    def test_good_agentic_contract_passes(self):
        submission = _base(
            profile="agentic",
            naturalLanguageIntent="A web API for customer data",
            confidenceAtSubmission=0.85,
            agentTrace="LLM generated contract from issue #42",
        )
        result = check_readiness(submission)
        self.assertTrue(result.ready, f"Expected ready, got: {result.reason_codes}")


class TestMissingTags(unittest.TestCase):
    def test_missing_tags_fail(self):
        submission = _base()
        submission["tags"] = {"nova:owner": "consumer-repo"}
        result = check_readiness(submission)
        self.assertFalse(result.ready)
        codes = " ".join(result.reason_codes)
        self.assertIn("MISSING_TAGS", codes)
        self.assertIn("nova:contract", codes)
        self.assertIn("nova:environment", codes)
        self.assertIn("nova:cost-center", codes)
        self.assertIn("nova:ref", codes)

    def test_empty_tag_value_fails(self):
        submission = _base()
        submission["tags"]["nova:owner"] = ""
        result = check_readiness(submission)
        self.assertFalse(result.ready)
        self.assertTrue(any("MISSING_TAGS" in c for c in result.reason_codes))


class TestEnvMissingMandatory(unittest.TestCase):
    def test_qa_missing_e2e_suite_fails(self):
        submission = _base(environment="qa")
        submission["tags"]["nova:environment"] = "qa"
        # No validation.e2eSuite
        result = check_readiness(submission)
        self.assertFalse(result.ready)
        codes = " ".join(result.reason_codes)
        self.assertIn("ENV_MISSING_MANDATORY:qa:validation.e2eSuite", codes)

    def test_prod_missing_runbook_fails(self):
        submission = _base(environment="prod")
        submission["tags"]["nova:environment"] = "prod"
        # No runbook/dashboard/oncall
        result = check_readiness(submission)
        self.assertFalse(result.ready)
        codes = " ".join(result.reason_codes)
        self.assertIn("ENV_MISSING_MANDATORY:prod:runbook", codes)
        self.assertIn("ENV_MISSING_MANDATORY:prod:dashboard", codes)
        self.assertIn("ENV_MISSING_MANDATORY:prod:oncall", codes)

    def test_dr_missing_drdrillref_fails(self):
        submission = _base(environment="dr")
        submission["tags"]["nova:environment"] = "dr"
        result = check_readiness(submission)
        self.assertFalse(result.ready)
        codes = " ".join(result.reason_codes)
        self.assertIn("ENV_MISSING_MANDATORY:dr:drDrillRef", codes)

    def test_prod_with_all_mandatory_passes(self):
        submission = _base(
            environment="prod",
            runbook="docs/runbooks/webapi.md",
            dashboard="https://grafana/nova/webapi",
            oncall="oncall@company.com",
        )
        submission["tags"]["nova:environment"] = "prod"
        result = check_readiness(submission)
        self.assertTrue(result.ready, f"Expected ready, got: {result.reason_codes}")


class TestAgenticMissingIntent(unittest.TestCase):
    def test_agentic_missing_all_markers_fails(self):
        submission = _base(profile="agentic")
        result = check_readiness(submission)
        self.assertFalse(result.ready)
        codes = " ".join(result.reason_codes)
        self.assertIn("AGENTIC_MISSING_INTENT:naturalLanguageIntent", codes)
        self.assertIn("AGENTIC_MISSING_INTENT:confidenceAtSubmission", codes)
        self.assertIn("AGENTIC_MISSING_INTENT:agentTrace", codes)

    def test_agentic_missing_one_marker_fails(self):
        submission = _base(
            profile="agentic",
            naturalLanguageIntent="A web API",
            confidenceAtSubmission=0.85,
            # agentTrace missing
        )
        result = check_readiness(submission)
        self.assertFalse(result.ready)
        self.assertTrue(any("agentTrace" in c for c in result.reason_codes))


class TestMissingAppSource(unittest.TestCase):
    def test_missing_appsource_fails(self):
        submission = _base()
        del submission["appSource"]
        result = check_readiness(submission)
        self.assertFalse(result.ready)
        self.assertTrue(any("MISSING_APP_SOURCE" in c for c in result.reason_codes))

    def test_appsource_missing_ref_fails(self):
        submission = _base()
        submission["appSource"] = {"repo": "consumer/repo"}
        result = check_readiness(submission)
        self.assertFalse(result.ready)
        self.assertTrue(any("MISSING_APP_SOURCE" in c for c in result.reason_codes))


class TestPolicyPreconditionMissing(unittest.TestCase):
    def test_empty_policy_fails(self):
        submission = _base()
        submission["policyPreconditions"] = {}
        result = check_readiness(submission)
        self.assertFalse(result.ready)
        self.assertTrue(any("POLICY_PRECONDITION_MISSING" in c for c in result.reason_codes))


class TestReadinessResultStructure(unittest.TestCase):
    def test_result_to_dict(self):
        result = check_readiness(_base())
        d = result.to_dict()
        self.assertIn("ready", d)
        self.assertIn("reason_codes", d)
        self.assertIn("contractId", d)

    def test_result_str_ready(self):
        result = check_readiness(_base())
        self.assertIn("READY", str(result))

    def test_result_str_not_ready(self):
        submission = _base()
        del submission["appSource"]
        result = check_readiness(submission)
        self.assertIn("NOT READY", str(result))


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