"""P03 W3 (REQ-319): adapter state-backend bucket resolution tests.

The adapter reads env.state_backend.bucket from the env onboarding JSON
(core/environments/<env>.json) when present, falling back to the computed
nova-tfstate-{account_id}-{region} pattern for backwards compat. dev is
bound to the real account 581513795199 + bucket
nova-tfstate-581513795199-us-east-1 (D-203); qa/prod/dr stay placeholder
(account_id 000000000000 — the pilot-readiness policy blocks apply on
placeholder, D-208).
"""
import json
import sys
from pathlib import Path

import pytest

sys.path.insert(0, str(Path(__file__).resolve().parent.parent))

from adapters.terraform.adapter import adapt, _resolve_state_bucket, _load_env_json

ROOT = Path(__file__).resolve().parent.parent


def _emit(env_name, tmp_path, **stack_overrides):
    """Run the adapter against a minimal s3 stack in the given environment."""
    stack = {
        "version": "1.0.0",
        "stack": {"name": "spike", "kind": "l1", "depth": 1, "environment": env_name},
        "resources": [
            {"id": "s3", "type": "aws:s3:bucket", "module": "s3@1.0.0",
             "inputs": {"bucket_name": "test", "region": "us-east-1"}}
        ],
    }
    stack.update(stack_overrides)
    adapt(stack, str(tmp_path))
    return (tmp_path / "terraform.tf").read_text()


class TestDevUsesRealStateBucket:
    def test_dev_uses_real_state_bucket(self, tmp_path):
        """dev.json is bound to the real account + bucket (D-203)."""
        tf = _emit("dev", tmp_path)
        assert 'bucket = "nova-tfstate-581513795199-us-east-1"' in tf

    def test_dev_account_id_is_real(self):
        env_json = _load_env_json("dev", str(ROOT))
        assert env_json["account_id"] == "581513795199"

    def test_dev_state_backend_bucket_matches_bootstrap(self):
        """The dev env JSON bucket matches the bootstrap-created bucket
        (terraform/bootstrap/create_state_backend.py +
        terraform/platform/main.tf)."""
        env_json = _load_env_json("dev", str(ROOT))
        assert env_json["state_backend"]["bucket"] == "nova-tfstate-581513795199-us-east-1"


class TestFallbackComputedName:
    def test_fallback_computed_name_when_no_state_backend(self):
        """An env JSON without state_backend.bucket → the adapter falls back
        to nova-tfstate-{account_id}-{region}."""
        env_json = {"account_id": "123456789012", "region": "us-west-2"}
        assert _resolve_state_bucket(env_json, "us-west-2") == "nova-tfstate-123456789012-us-west-2"

    def test_fallback_uses_account_id_from_env_json(self, tmp_path):
        """When state_backend.bucket is absent, the computed name uses
        account_id from the env JSON (not a hardcoded default)."""
        env_json = {"account_id": "999999999999", "region": "us-east-1"}
        assert _resolve_state_bucket(env_json, "us-east-1") == "nova-tfstate-999999999999-us-east-1"

    def test_fallback_to_real_account_when_account_id_absent(self):
        """When account_id is also absent, fall back to the only real
        account (581513795199 — the bootstrap bucket)."""
        env_json = {}
        assert _resolve_state_bucket(env_json, "us-east-1") == "nova-tfstate-581513795199-us-east-1"

    def test_empty_env_json_falls_back(self, tmp_path):
        """An env JSON with no state_backend block at all → computed name."""
        # Use an environment name with no JSON file → _load_env_json returns {}.
        tf = _emit("nonexistent-env", tmp_path)
        assert "nova-tfstate-581513795199-us-east-1" in tf

    def test_empty_bucket_string_falls_back(self):
        """An empty state_backend.bucket string → fall back to computed name."""
        env_json = {"account_id": "111111111111", "region": "eu-west-1",
                     "state_backend": {"bucket": "", "lock_table": "x"}}
        assert _resolve_state_bucket(env_json, "eu-west-1") == "nova-tfstate-111111111111-eu-west-1"


class TestQaPlaceholderAccount:
    def test_qa_placeholder_account(self, tmp_path):
        """qa env JSON has account_id 000000000000 (placeholder, D-208) —
        the pilot-readiness policy blocks apply on placeholder. The adapter
        still emits the computed bucket name with the placeholder account."""
        tf = _emit("qa", tmp_path)
        # qa.json has state_backend.bucket = nova-tfstate-000000000000-us-east-1
        assert 'bucket = "nova-tfstate-000000000000-us-east-1"' in tf

    def test_qa_account_id_is_placeholder(self):
        env_json = _load_env_json("qa", str(ROOT))
        assert env_json["account_id"] == "000000000000"

    def test_prod_account_id_is_placeholder(self):
        env_json = _load_env_json("prod", str(ROOT))
        assert env_json["account_id"] == "000000000000"

    def test_dr_account_id_is_placeholder(self):
        env_json = _load_env_json("dr", str(ROOT))
        assert env_json["account_id"] == "000000000000"


class TestStateKeyEnvScoped:
    def test_state_key_remains_env_scoped(self, tmp_path):
        """The state key path stays env-scoped:
        spike/{stack_name}/{environment}/terraform.tfstate (REQ-287)."""
        tf = _emit("dev", tmp_path, **{
            "version": "1.0.0",
            "stack": {"name": "msvc", "kind": "l2", "depth": 1, "environment": "dev"},
            "resources": [
                {"id": "s3", "type": "aws:s3:bucket", "module": "s3@1.0.0",
                 "inputs": {"bucket_name": "test", "region": "us-east-1"}}
            ],
        })
        assert "spike/msvc/dev/terraform.tfstate" in tf


class TestDynamodbL1Emission:
    """W3 Task 3.5: the dynamodb L1 primitive (landed in P2, REQ-322)
    resolves + emits an aws_dynamodb_table module block with PK block_index,
    PAY_PER_REQUEST."""

    def test_dynamodb_resolves_and_emits_module_block(self, tmp_path):
        from core.contract_resolver import resolve
        # Resolve a contract with an infrastructure.dynamodb block.
        contract = {
            "id": "ddb", "name": "dynamodb-test", "environment": "dev",
            "infrastructure": {
                "dynamodb": {
                    "version": "1.0.0",
                    "inputs": {
                        "table_name": "nova-blockchain-ledger",
                        "region": "us-east-1",
                        "pk": "block_index",
                        "billing_mode": "PAY_PER_REQUEST",
                    },
                },
            },
        }
        contract_path = tmp_path / "ddb.yml"
        import yaml
        contract_path.write_text(yaml.safe_dump(contract))
        stack = resolve(str(contract_path), str(ROOT))
        # The stack has one dynamodb resource. The resource id is derived
        # from the interface type (aws:dynamodb:table → "table").
        ddb = [r for r in stack["resources"] if r["type"] == "aws:dynamodb:table"]
        assert len(ddb) == 1
        assert ddb[0]["inputs"]["pk"] == "block_index"
        assert ddb[0]["inputs"]["billing_mode"] == "PAY_PER_REQUEST"
        # Emit Terraform.
        adapt(stack, str(tmp_path))
        main_tf = (tmp_path / "main.tf").read_text()
        assert 'module "table" {' in main_tf
        assert 'pk = "block_index"' in main_tf
        assert 'billing_mode = "PAY_PER_REQUEST"' in main_tf
        # The module source points at the dynamodb terraform dir.
        assert "modules/l1/dynamodb/terraform" in main_tf

    def test_dynamodb_instance_emits_valid_terraform(self, tmp_path):
        """The dynamodb L1 instance.json emits terraform that passes
        terraform init + validate (the real regression gate)."""
        import subprocess
        instance = json.load(open(ROOT / "modules/l1/dynamodb/instance.json"))
        # The instance.json is a module-inputs file, not a stack instance —
        # build a minimal stack instance wrapping it.
        stack = {
            "version": "1.0.0",
            "stack": {"name": "ddb", "kind": "l1", "depth": 1, "environment": "dev"},
            "resources": [
                {"id": "dynamodb", "type": "aws:dynamodb:table", "module": "dynamodb@1.0.0",
                 "inputs": instance["inputs"]}
            ],
        }
        adapt(stack, str(tmp_path))
        result = subprocess.run(
            ["terraform", "init", "-backend=false", "-input=false"],
            cwd=str(tmp_path), capture_output=True, text=True
        )
        assert result.returncode == 0, f"terraform init failed: {result.stderr}"
        result = subprocess.run(
            ["terraform", "validate"],
            cwd=str(tmp_path), capture_output=True, text=True
        )
        assert result.returncode == 0, f"terraform validate failed: {result.stderr}"
        main_tf = (tmp_path / "main.tf").read_text()
        assert 'module "dynamodb" {' in main_tf
        assert 'pk = "block_index"' in main_tf