"""Standalone Platform App Standard v1 checks."""

from __future__ import annotations

import sys
import unittest
from pathlib import Path
from unittest.mock import patch

PROJECT_ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(PROJECT_ROOT))
loaded_platform = sys.modules.get("platform")
if loaded_platform is not None and not hasattr(loaded_platform, "__path__"):
    del sys.modules["platform"]

from platform.apps.contracts import (
    ActionContract,
    AppManifest,
    CredentialField,
    WordPressPackageContract,
)
from platform.apps.envelopes import error_envelope, success_envelope
from platform.apps.registry import resolve_capability


class AppContractChecks(unittest.TestCase):
    @staticmethod
    def _health_action():
        return ActionContract(
            capability="platform.health",
            description="Report local health.",
            output_schema={"type": "object", "required": ["healthy"]},
        )

    def test_valid_manifest_and_credential_reference(self):
        manifest = AppManifest(
            app_id="caller_app", display_name="Caller", app_version="1.0.0",
            description="Test caller application.",
            credentials=(CredentialField("api_token", "API token"),),
            actions={"health_v1": ActionContract(
                capability="platform.health", description="Health",
                output_schema={"type": "object", "required": ["healthy"]},
            ), "test_connection_v1": ActionContract(
                capability="platform.credentials.test", description="Connection test",
                required_credentials=("api_token",),
                output_schema={"type": "object", "required": ["connected", "provider"]},
            )},
        )
        self.assertEqual([], manifest.validate())

    def test_unknown_credential_is_rejected(self):
        manifest = AppManifest(
            app_id="bad_app", display_name="Bad", app_version="1.0.0",
            description="Invalid credential reference test.",
            actions={"lookup_v1": ActionContract(
                capability="records.lookup", description="Lookup",
                required_credentials=("missing",),
                output_schema={"type": "object", "required": ["record"]},
            ), "health_v1": ActionContract(
                capability="platform.health", description="Health",
                output_schema={"type": "object", "required": ["healthy"]},
            )},
        )
        self.assertTrue(any("unknown credentials" in error for error in manifest.validate()))

    def test_capability_requires_explicit_outbound_permission(self):
        caller = AppManifest(
            app_id="caller_app", display_name="Caller", app_version="1.0.0",
            description="Capability caller test.",
            outbound_capabilities=("target_app:records.lookup",),
            actions={"health_v1": ActionContract(
                capability="platform.health", description="Health",
                output_schema={"type": "object", "required": ["healthy"]},
            )},
        )
        target = AppManifest(
            app_id="target_app", display_name="Target", app_version="1.0.0",
            description="Capability target test.",
            actions={
                "health_v1": ActionContract(
                    capability="platform.health", description="Health",
                    output_schema={"type": "object", "required": ["healthy"]},
                ),
                "lookup_v1": ActionContract(
                    capability="records.lookup", description="Lookup",
                    output_schema={"type": "object", "required": ["record"]},
                ),
            },
        )
        with patch("platform.apps.registry.load_manifest", side_effect=[caller, target]):
            self.assertEqual(
                "lookup_v1",
                resolve_capability("caller_app", "target_app", "records.lookup"),
            )
        denied = AppManifest(
            app_id="caller_app", display_name="Caller", app_version="1.0.0",
            description="Denied capability caller test.",
            actions={"health_v1": ActionContract(
                capability="platform.health", description="Health",
                output_schema={"type": "object", "required": ["healthy"]},
            )},
        )
        with patch("platform.apps.registry.load_manifest", side_effect=[denied, target]):
            with self.assertRaises(PermissionError):
                resolve_capability("caller_app", "target_app", "records.lookup")

    def test_envelopes_never_mix_data_and_error(self):
        success = success_envelope(
            app_id="app_a", action="lookup", request_id="request-1", data={"id": 1},
        )
        failure = error_envelope(
            app_id="app_a", action="lookup", request_id="request-1",
            code="record_not_found", message="Record not found",
        )
        self.assertIsNone(success["error"])
        self.assertIsNone(failure["data"])
        self.assertEqual("1.0", success["contract_version"])

    def test_wordpress_pack_cannot_use_undeclared_capability(self):
        manifest = AppManifest(
            app_id="pack_app", display_name="Pack", app_version="1.0.0",
            description="Invalid package capability test.",
            actions={"health_v1": ActionContract(
                capability="platform.health", description="Health",
                output_schema={"type": "object", "required": ["healthy"]},
            )},
            wordpress_package=WordPressPackageContract(
                package_id="pack_app_wp", version="1.0.0", source_directory="wordpress-pack/pack",
                entrypoint="pack.php", plugin_file="pack/pack.php", capabilities=("records.lookup",),
            ),
        )
        self.assertTrue(any("unknown capabilities" in error for error in manifest.validate()))

    def test_partial_release_metadata_is_rejected(self):
        manifest = AppManifest(
            app_id="pack_app", display_name="Pack", app_version="1.0.0",
            description="Partial package metadata test.",
            actions={"health_v1": ActionContract(
                capability="platform.health", description="Health",
                output_schema={"type": "object", "required": ["healthy"]},
            )},
            wordpress_package=WordPressPackageContract(
                package_id="pack_app_wp", version="1.0.0", source_directory="wordpress-pack/pack",
                entrypoint="pack.php", plugin_file="pack/pack.php", capabilities=("platform.health",),
                artifact_name="pack.zip",
            ),
        )
        self.assertTrue(any("release metadata" in error for error in manifest.validate()))

    def test_semver_and_versioned_actions_are_required(self):
        manifest = AppManifest(
            app_id="bad_app",
            display_name="Bad version",
            app_version="one",
            description="Invalid versioning test.",
            actions={"health": self._health_action()},
        )
        errors = manifest.validate()
        self.assertTrue(any("semantic versioning" in error for error in errors))
        self.assertTrue(any("contract suffix" in error for error in errors))

    def test_output_schema_and_error_codes_are_validated(self):
        manifest = AppManifest(
            app_id="bad_app",
            display_name="Bad schema",
            app_version="1.0.0",
            description="Invalid schema and error test.",
            actions={
                "health_v1": self._health_action(),
                "lookup_v1": ActionContract(
                    capability="records.lookup",
                    description="Lookup",
                    output_schema={"type": "array"},
                    errors=("Bad-Code",),
                ),
            },
        )
        errors = manifest.validate()
        self.assertTrue(any("output_schema" in error for error in errors))
        self.assertTrue(any("invalid error code" in error for error in errors))

    def test_non_idempotent_side_effect_requires_key(self):
        manifest = AppManifest(
            app_id="bad_app",
            display_name="Bad side effect",
            app_version="1.0.0",
            description="Invalid idempotency test.",
            actions={
                "health_v1": self._health_action(),
                "create_v1": ActionContract(
                    capability="records.create",
                    description="Create",
                    output_schema={"type": "object", "required": ["record_id"]},
                    side_effects=True,
                    idempotent=False,
                ),
            },
        )
        self.assertTrue(
            any("idempotency key" in error for error in manifest.validate())
        )

    def test_wordpress_plugin_file_must_match_package_root(self):
        manifest = AppManifest(
            app_id="pack_app",
            display_name="Pack",
            app_version="1.0.0",
            description="Invalid WordPress plugin identity test.",
            actions={"health_v1": self._health_action()},
            wordpress_package=WordPressPackageContract(
                package_id="pack_app_wp",
                version="1.0.0",
                source_directory="wordpress-pack/pack",
                entrypoint="pack.php",
                plugin_file="another/pack.php",
                capabilities=("platform.health",),
            ),
        )
        self.assertTrue(
            any("plugin_file" in error for error in manifest.validate())
        )


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