"""Tests for environment variable expansion in configuration."""

import os
import unittest
from unittest import TestCase

from make_post_sell import get_children_settings


class TestEnvironmentVariableExpansion(unittest.TestCase):
    """Test environment variable expansion with ${VAR:-default} syntax."""

    def setUp(self):
        """Set up test environment."""
        # Store original environment
        self.original_env = os.environ.copy()
        # Clear any test variables
        for key in list(os.environ.keys()):
            if key.startswith("TEST_"):
                del os.environ[key]

    def tearDown(self):
        """Restore original environment."""
        os.environ.clear()
        os.environ.update(self.original_env)

    def test_simple_expansion_with_value(self):
        """Test ${VAR} expansion when variable is set."""
        os.environ["TEST_VAR"] = "test_value"
        settings = {"app.test.setting": "${TEST_VAR}", "app.other": "static"}
        result = get_children_settings(settings, "app")
        self.assertEqual(result["test.setting"], "test_value")
        self.assertEqual(result["other"], "static")

    def test_expansion_with_default_used(self):
        """Test ${VAR:-default} uses default when var is not set."""
        # Ensure TEST_MISSING is not in environment
        if "TEST_MISSING" in os.environ:
            del os.environ["TEST_MISSING"]

        settings = {
            "app.payment.enabled": "${TEST_MISSING:-true}",
            "app.payment.provider": "${TEST_MISSING:-stripe}",
        }
        result = get_children_settings(settings, "app")
        self.assertEqual(
            result["payment.enabled"], True
        )  # get_int_or_bool_or_none_or_str converts
        self.assertEqual(result["payment.provider"], "stripe")

    def test_expansion_with_default_not_used(self):
        """Test ${VAR:-default} uses env value when var is set."""
        os.environ["TEST_PAYMENT_ENABLED"] = "false"
        os.environ["TEST_PROVIDER"] = "monero"

        settings = {
            "app.payment.enabled": "${TEST_PAYMENT_ENABLED:-true}",
            "app.payment.provider": "${TEST_PROVIDER:-stripe}",
        }
        result = get_children_settings(settings, "app")
        self.assertEqual(
            result["payment.enabled"], False
        )  # get_int_or_bool_or_none_or_str converts
        self.assertEqual(result["payment.provider"], "monero")

    def test_empty_default_value(self):
        """Test ${VAR:-} with empty default."""
        settings = {
            "app.optional.setting": "${TEST_OPTIONAL:-}",
            "app.optional.another": "${TEST_ANOTHER:-}",
        }
        result = get_children_settings(settings, "app")
        self.assertEqual(result["optional.setting"], "")
        self.assertEqual(result["optional.another"], "")

    def test_boolean_conversion(self):
        """Test boolean string conversion."""
        settings = {
            "app.bool.true1": "${TEST_BOOL:-true}",
            "app.bool.true2": "${TEST_BOOL:-True}",
            "app.bool.true3": "${TEST_BOOL:-1}",
            "app.bool.true4": "${TEST_BOOL:-yes}",
            "app.bool.false1": "${TEST_BOOL:-false}",
            "app.bool.false2": "${TEST_BOOL:-False}",
            "app.bool.false3": "${TEST_BOOL:-0}",
            "app.bool.false4": "${TEST_BOOL:-no}",
        }
        result = get_children_settings(settings, "app")

        # All true values
        self.assertEqual(result["bool.true1"], True)
        self.assertEqual(result["bool.true2"], True)
        self.assertEqual(result["bool.true3"], True)
        self.assertEqual(result["bool.true4"], True)

        # All false values
        self.assertEqual(result["bool.false1"], False)
        self.assertEqual(result["bool.false2"], False)
        self.assertEqual(result["bool.false3"], False)
        self.assertEqual(result["bool.false4"], False)

    def test_integer_conversion(self):
        """Test integer string conversion."""
        settings = {
            "app.number.int1": "${TEST_NUM:-123}",
            "app.number.int2": "${TEST_NUM:-0}",
            "app.number.negative": "${TEST_NUM:--456}",
        }
        result = get_children_settings(settings, "app")

        self.assertEqual(result["number.int1"], 123)
        self.assertEqual(result["number.int2"], 0)
        self.assertEqual(result["number.negative"], -456)

    def test_none_conversion(self):
        """Test None string conversion."""
        settings = {
            "app.nullable.none1": "${TEST_NULL:-None}",
            "app.nullable.none2": "${TEST_NULL:-none}",
            "app.nullable.null": "${TEST_NULL:-null}",
            "app.nullable.null2": "${TEST_NULL:-NULL}",
        }
        result = get_children_settings(settings, "app")

        self.assertIsNone(result["nullable.none1"])
        self.assertIsNone(result["nullable.none2"])
        self.assertIsNone(result["nullable.null"])
        self.assertIsNone(result["nullable.null2"])

    def test_mixed_syntax(self):
        """Test mixing of different variable syntaxes."""
        os.environ["TEST_SIMPLE"] = "simple_value"
        os.environ["TEST_BRACES"] = "braces_value"
        os.environ["TEST_DEFAULT_OVERRIDE"] = "override_value"

        settings = {
            "app.var.simple": "$TEST_SIMPLE",
            "app.var.braces": "${TEST_BRACES}",
            "app.var.default_used": "${TEST_MISSING:-default_value}",
            "app.var.default_override": "${TEST_DEFAULT_OVERRIDE:-default_value}",
        }
        result = get_children_settings(settings, "app")

        self.assertEqual(result["var.simple"], "simple_value")
        self.assertEqual(result["var.braces"], "braces_value")
        self.assertEqual(result["var.default_used"], "default_value")
        self.assertEqual(result["var.default_override"], "override_value")

    def test_complex_default_values(self):
        """Test default values with special characters."""
        settings = {
            "app.url.default": "${TEST_URL:-http://localhost:8080}",
            "app.path.default": "${TEST_PATH:-/path/to/file}",
            "app.json.default": '${TEST_JSON:-{"key": "value"}}',
            "app.special.chars": "${TEST_SPECIAL:-value-with_special.chars}",
        }
        result = get_children_settings(settings, "app")

        self.assertEqual(result["url.default"], "http://localhost:8080")
        self.assertEqual(result["path.default"], "/path/to/file")
        self.assertEqual(result["json.default"], '{"key": "value"}')
        self.assertEqual(result["special.chars"], "value-with_special.chars")

    def test_nested_variables_not_expanded(self):
        """Test that nested variable references are not expanded."""
        os.environ["TEST_INNER"] = "inner_value"

        settings = {
            "app.nested.var": "${TEST_OUTER:-$TEST_INNER}",
        }
        result = get_children_settings(settings, "app")

        # Should not expand the inner $TEST_INNER
        self.assertEqual(result["nested.var"], "$TEST_INNER")

    def test_multiple_variables_in_value(self):
        """Test multiple variable references in a single value."""
        os.environ["TEST_HOST"] = "localhost"
        os.environ["TEST_PORT"] = "8080"

        settings = {
            "app.url.composite": "${TEST_PROTO:-http}://${TEST_HOST}:${TEST_PORT}/path",
            "app.mixed.defaults": "${TEST_USER:-admin}:${TEST_PASS:-secret}@${TEST_HOST}",
        }
        result = get_children_settings(settings, "app")

        self.assertEqual(result["url.composite"], "http://localhost:8080/path")
        self.assertEqual(result["mixed.defaults"], "admin:secret@localhost")

    def test_non_string_values_unchanged(self):
        """Test that non-string values pass through unchanged."""
        settings = {
            "app.already.bool": True,
            "app.already.int": 42,
            "app.already.none": None,
            "app.already.list": ["a", "b", "c"],
        }
        result = get_children_settings(settings, "app")

        self.assertEqual(result["already.bool"], True)
        self.assertEqual(result["already.int"], 42)
        self.assertIsNone(result["already.none"])
        self.assertEqual(result["already.list"], ["a", "b", "c"])

    def test_parent_key_filtering(self):
        """Test that only settings with parent key are returned."""
        settings = {
            "app.included.one": "${TEST_VAR:-value1}",
            "app.included.two": "${TEST_VAR:-value2}",
            "other.excluded.one": "${TEST_VAR:-excluded1}",
            "session.excluded.two": "${TEST_VAR:-excluded2}",
        }
        result = get_children_settings(settings, "app")

        self.assertIn("included.one", result)
        self.assertIn("included.two", result)
        self.assertNotIn("excluded.one", result)
        self.assertNotIn("excluded.two", result)
        self.assertEqual(len(result), 2)

    def test_whitespace_handling(self):
        """Test handling of whitespace in values."""
        os.environ["TEST_TRIMMED"] = "  trimmed  "

        settings = {
            "app.space.env": "${TEST_TRIMMED}",
            "app.space.default": "${TEST_MISSING:-  spaced  }",
            "app.space.empty": "${TEST_MISSING:-}",
        }
        result = get_children_settings(settings, "app")

        # Values should be preserved as-is
        self.assertEqual(result["space.env"], "  trimmed  ")
        self.assertEqual(result["space.default"], "  spaced  ")
        self.assertEqual(result["space.empty"], "")

    def test_edge_cases(self):
        """Test various edge cases."""
        settings = {
            "app.edge.dollar_only": "$",
            "app.edge.unclosed_brace": "${TEST_VAR",
            "app.edge.no_var_name": "${}",
            "app.edge.just_default": "${:-default}",
            "app.edge.multiple_colons": "${TEST_VAR:-value:with:colons}",
            "app.edge.brace_in_default": "${TEST_VAR:-value}with}brace}",
        }
        result = get_children_settings(settings, "app")

        # These should remain unchanged or handle gracefully
        self.assertEqual(result["edge.dollar_only"], "$")
        self.assertEqual(result["edge.unclosed_brace"], "${TEST_VAR")
        self.assertEqual(result["edge.no_var_name"], "${}")
        self.assertEqual(result["edge.just_default"], "default")
        self.assertEqual(result["edge.multiple_colons"], "value:with:colons")
        # The regex stops at the first closing brace, so it parses as ${TEST_VAR:-value} + "with}brace}"
        self.assertEqual(result["edge.brace_in_default"], "valuewith}brace}")


class TestPaymentConfigurationExpansion(unittest.TestCase):
    """Test specific payment configuration scenarios."""

    def setUp(self):
        """Set up test environment."""
        self.original_env = os.environ.copy()
        # Clear any test variables
        for key in list(os.environ.keys()):
            if key.startswith("MPS_"):
                del os.environ[key]

    def tearDown(self):
        """Restore original environment."""
        os.environ.clear()
        os.environ.update(self.original_env)

    def test_stripe_monero_defaults(self):
        """Test default payment configuration values."""
        settings = {
            "app.payments.stripe.enabled": "${MPS_PAYMENTS_STRIPE_ENABLED:-true}",
            "app.payments.monero.enabled": "${MPS_PAYMENTS_MONERO_ENABLED:-false}",
        }
        result = get_children_settings(settings, "app")

        self.assertEqual(result["payments.stripe.enabled"], True)
        self.assertEqual(result["payments.monero.enabled"], False)

    def test_stripe_monero_overrides(self):
        """Test overriding payment configuration."""
        os.environ["MPS_PAYMENTS_STRIPE_ENABLED"] = "false"
        os.environ["MPS_PAYMENTS_MONERO_ENABLED"] = "true"

        settings = {
            "app.payments.stripe.enabled": "${MPS_PAYMENTS_STRIPE_ENABLED:-true}",
            "app.payments.monero.enabled": "${MPS_PAYMENTS_MONERO_ENABLED:-false}",
        }
        result = get_children_settings(settings, "app")

        self.assertEqual(result["payments.stripe.enabled"], False)
        self.assertEqual(result["payments.monero.enabled"], True)

    def test_monero_configuration(self):
        """Test Monero RPC configuration expansion."""
        os.environ["MPS_MONERO_RPC_URL"] = "http://192.168.1.100:18082/json_rpc"
        os.environ["MPS_MONERO_RPC_USER"] = "monero_user"
        os.environ["MPS_MONERO_RPC_PASS"] = "monero_pass"

        settings = {
            "monero.rpc_url": "${MPS_MONERO_RPC_URL:-http://127.0.0.1:18083/json_rpc}",
            "monero.rpc_user": "${MPS_MONERO_RPC_USER:-}",
            "monero.rpc_pass": "${MPS_MONERO_RPC_PASS:-}",
        }
        result = get_children_settings(settings, "monero")

        self.assertEqual(result["rpc_url"], "http://192.168.1.100:18082/json_rpc")
        self.assertEqual(result["rpc_user"], "monero_user")
        self.assertEqual(result["rpc_pass"], "monero_pass")
