#!/usr/bin/env python3
"""
Tests for ``decide_for_version`` floor-vs-target scenarios.

Covers what happens when the project's MARKETING_VERSION sits below /
above / equal to the ASC combined floor (max of appStoreVersions +
preReleaseVersions + buildsViaPreRelease):

  * floor strictly above target with editable at target -> SystemExit(2)
    (must NOT silently downgrade)
  * floor equals target with no row at target -> SystemExit(2)
    (CREATE would collide)
  * floor None (true first-release) -> CREATE proceeds
  * target below floor where floor came from a higher editable ->
    SystemExit(2)
  * target strictly above the floor -> CREATE
  * target equal to a terminal floor row -> SystemExit(2)
    (the auto-roll variant of this lives in
    test_manage_marketing_version_autoroll.py)
  * target below a terminal row -> SystemExit(2)

Match-at-target scenarios live in
``test_manage_marketing_version_match.py``. main() / env-validation
scenarios stay in ``test_manage_marketing_version.py``. Split out in
round 8 to keep each test file under the 10-functions-per-file cap.
"""

from __future__ import annotations

import os
import sys
import unittest
from pathlib import Path
from unittest import mock

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

from tests_common import (  # noqa: E402
    _assert_decide_exits,
    _assert_mentions_build_setting_source,
    _run_decide_for_version,
    _v,
)


def setUpModule() -> None:
    """Pin auto-bump to 'none' for the whole floor-tests module.

    The floor-vs-target tests assert the historical fail-on-floor /
    historical reject behavior. With auto-bump enabled (the default
    'patch'), the round-7 auto-roll would CREATE the next patch
    instead of rejecting on terminal collisions, which is a
    different code path covered by
    ``test_manage_marketing_version_autoroll.py``.

    Pinning the env via setUpModule (rather than per-test) keeps each
    test pure -- they exercise floor reasoning, not policy gating."""
    global _ENV_PATCH
    _ENV_PATCH = mock.patch.dict(
        os.environ, {"MARKETING_VERSION_AUTO_BUMP": "none"}, clear=False,
    )
    _ENV_PATCH.start()


def tearDownModule() -> None:
    _ENV_PATCH.stop()


class FloorVsTargetTests(unittest.TestCase):
    """``decide_for_version`` floor-vs-target compare scenarios."""

    def test_reuse_blocked_when_floor_strictly_above_target(self):
        """Issue 3 regression: editable matches target but floor came
        from a HIGHER source. REUSE must NOT silently downgrade."""
        editable = _v("1.0.0", "PREPARE_FOR_SUBMISSION", vid="ed-100")
        per_source = {
            "appStoreVersions": "2.0.0",
            "preReleaseVersions": "2.0.0",
            "buildsViaPreRelease": None,
        }
        stderr = _assert_decide_exits(
            self, "1.0.0", [editable],
            ground_truth="2.0.0", combined_floor="2.0.0",
            per_source=per_source,
        )
        self.assertIn("1.0.0", stderr)
        self.assertIn("2.0.0", stderr)
        _assert_mentions_build_setting_source(self, stderr)

    def test_create_blocked_when_floor_equals_target_no_match(self):
        """No editable at target but floor equals target -- CREATE
        blocked (would collide with whatever pinned the floor)."""
        per_source = {
            "appStoreVersions": None,
            "preReleaseVersions": "1.0.0",
            "buildsViaPreRelease": None,
        }
        stderr = _assert_decide_exits(
            self, "1.0.0", [],
            ground_truth=None, combined_floor="1.0.0",
            per_source=per_source,
        )
        self.assertIn("strictly greater than", stderr)
        self.assertIn("1.0.0", stderr)
        _assert_mentions_build_setting_source(self, stderr)

    def test_create_proceeds_when_floor_none_and_target_present(self):
        """First-release scenario: ASC empty, target 1.0.0 -> CREATE 1.0.0."""
        result, stderr, calls = _run_decide_for_version(
            "1.0.0", versions=[],
            ground_truth=None, combined_floor=None,
        )
        self.assertEqual(result["decision"], "CREATE")
        self.assertEqual(result["versionString"], "1.0.0")
        self.assertEqual(len(calls), 1)
        self.assertEqual(calls[0].args[1], "1.0.0")
        self.assertIn("decision=CREATE", stderr)

    def test_target_below_floor_with_editable_at_floor_exits_2(self):
        """Scenario 3: target 1.0.0 + editable PFS at 1.0.6 with combined
        floor 1.0.6 -> SystemExit(2) with build-setting source mention."""
        editable = _v("1.0.6", "PREPARE_FOR_SUBMISSION", vid="ed-106")
        stderr = _assert_decide_exits(
            self, "1.0.0", [editable], ground_truth="1.0.6",
        )
        self.assertIn("1.0.0", stderr)
        self.assertIn("1.0.6", stderr)
        _assert_mentions_build_setting_source(self, stderr)

    def test_target_strictly_above_floor_creates(self):
        """Scenario 4: target 1.0.5 + appStoreVersions [1.0.4 RFS] -> CREATE 1.0.5."""
        versions = [_v("1.0.4", "READY_FOR_SALE", vid="rfs-104")]
        result, stderr, calls = _run_decide_for_version(
            "1.0.5", versions=versions, ground_truth="1.0.4",
        )
        self.assertEqual(result["decision"], "CREATE")
        self.assertEqual(result["versionString"], "1.0.5")
        self.assertEqual(len(calls), 1)
        self.assertEqual(calls[0].args[1], "1.0.5")
        self.assertIn("decision=CREATE", stderr)

    def test_target_equal_to_terminal_exits_2(self):
        """Scenario 5: target 1.0.4 + appStoreVersions [1.0.4 RFS] ->
        SystemExit(2). When auto-bump policy='none' (the default in this
        test fixture's _run_decide_for_version helper), no auto-roll
        fires, so the historical reject path applies.

        The auto-roll variant (same scenario but auto-bump=patch ->
        CREATE 1.0.5) lives in
        test_manage_marketing_version_autoroll.py."""
        versions = [_v("1.0.4", "READY_FOR_SALE", vid="rfs-104")]
        stderr = _assert_decide_exits(
            self, "1.0.4", versions, ground_truth="1.0.4",
        )
        self.assertIn("1.0.4", stderr)
        _assert_mentions_build_setting_source(self, stderr)

    def test_target_below_terminal_exits_2(self):
        """Scenario 6: target 1.0.3 + appStoreVersions [1.0.4 RFS] -> SystemExit(2)."""
        versions = [_v("1.0.4", "READY_FOR_SALE", vid="rfs-104")]
        stderr = _assert_decide_exits(
            self, "1.0.3", versions, ground_truth="1.0.4",
        )
        self.assertIn("1.0.3", stderr)
        self.assertIn("1.0.4", stderr)
        _assert_mentions_build_setting_source(self, stderr)


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