"""Named flakiness fix strategies.

Provides explicit strategy classes for different flakiness types
as documented in currentfeatures.md.
"""

from __future__ import annotations

import abc
from dataclasses import dataclass, field
from enum import Enum
from typing import Any, Dict, List, Optional


class FlakinessCategory(str, Enum):
    """Categories of test flakiness."""

    TIMING = "timing"  # Race conditions, async issues
    ORDERING = "ordering"  # Nondeterministic order
    RESOURCE = "resource"  # Port conflicts, file locks
    STATE = "state"  # Shared/leaked state
    NETWORK = "network"  # External service issues
    CONCURRENCY = "concurrency"  # Thread safety
    FLOATING_POINT = "floating_point"  # IEEE 754 comparison issues
    RANDOMNESS = "randomness"  # Unseeded or nondeterministic randomness
    PLATFORM = "platform"  # OS/path/line-ending differences
    ENVIRONMENT = "environment"  # CI/locale/container differences
    UNKNOWN = "unknown"


@dataclass
class StrategyFix:
    """A fix produced by a strategy."""

    diff: str
    description: str
    confidence: float  # 0.0-1.0
    file_path: Optional[str] = None
    reasoning: str = ""


@dataclass
class StrategyContext:
    """Context for strategy execution."""

    # Code context
    test_code: str
    source_code: Optional[str] = None
    file_path: Optional[str] = None

    # Error context
    error_type: Optional[str] = None
    error_message: Optional[str] = None
    divergence_type: Optional[str] = None

    # Analysis results
    root_cause: Optional[str] = None
    affected_lines: List[int] = field(default_factory=list)

    # Transcripts
    http_calls: List[Dict[str, Any]] = field(default_factory=list)
    sql_queries: List[Dict[str, Any]] = field(default_factory=list)
    redis_commands: List[Dict[str, Any]] = field(default_factory=list)


class BaseStrategy(abc.ABC):
    """Abstract base class for flakiness fix strategies."""

    name: str = "base"
    category: FlakinessCategory
    description: str = ""
    priority: int = 5  # 1-10, higher = try first

    @abc.abstractmethod
    def applies_to(self, context: StrategyContext) -> bool:
        """Check if this strategy applies to the given context.

        Args:
            context: Strategy context with code and error info

        Returns:
            True if strategy may help fix this flakiness
        """
        ...

    @abc.abstractmethod
    def generate_fix(self, context: StrategyContext) -> Optional[StrategyFix]:
        """Generate a fix for the flakiness.

        Args:
            context: Strategy context

        Returns:
            StrategyFix if a fix can be generated, None otherwise
        """
        ...


class TimingStrategy(BaseStrategy):
    """Strategy for timing-related flakiness.

    Addresses:
    - Race conditions
    - Missing await
    - time.sleep() issues
    - Timeout problems
    """

    name = "timing"
    category = FlakinessCategory.TIMING
    description = "Fix race conditions and async timing issues"
    priority = 8

    def applies_to(self, context: StrategyContext) -> bool:
        """Check for timing-related patterns."""
        indicators = [
            "time.sleep" in context.test_code,
            "async def" in context.test_code and "await" not in context.test_code,
            "asyncio" in context.test_code,
            "threading" in context.test_code,
            context.divergence_type in ("TIMING_DIFF", "RACE_CONDITION"),
            "timeout" in (context.error_message or "").lower(),
        ]
        return any(indicators)

    def generate_fix(self, context: StrategyContext) -> Optional[StrategyFix]:
        """Generate timing fix."""
        import re

        # Pattern: Missing await
        if "async def" in context.test_code:
            # Find async calls without await
            async_call_pattern = r"(\s+)(\w+)\.(fetch|get|post|execute|query)\("
            match = re.search(async_call_pattern, context.test_code)
            if match:
                indent = match.group(1)
                return StrategyFix(
                    diff=f"""-{indent}{match.group(2)}.{match.group(3)}(
+{indent}await {match.group(2)}.{match.group(3)}(""",
                    description="Add missing await to async call",
                    confidence=0.85,
                    reasoning="Async calls must be awaited to ensure proper ordering",
                )

        # Pattern: Hard-coded sleep
        if "time.sleep" in context.test_code:
            return StrategyFix(
                diff="""-import time
+from tenacity import retry, wait_exponential, stop_after_attempt

-time.sleep(1)
+@retry(wait=wait_exponential(min=0.1, max=2), stop=stop_after_attempt(5))
+def wait_for_condition():
+    assert condition_met()""",
                description="Replace time.sleep with retry-based waiting",
                confidence=0.75,
                reasoning="Hard-coded sleeps are flaky; use condition-based waiting",
            )

        return None


class OrderingStrategy(BaseStrategy):
    """Strategy for ordering-related flakiness.

    Addresses:
    - SQL without ORDER BY
    - Dict/set iteration order
    - Nondeterministic collection processing
    """

    name = "ordering"
    category = FlakinessCategory.ORDERING
    description = "Fix nondeterministic ordering issues"
    priority = 9

    def applies_to(self, context: StrategyContext) -> bool:
        """Check for ordering-related patterns."""
        indicators = [
            # SQL without ORDER BY
            any(
                "select" in q.get("query", "").lower()
                and "order by" not in q.get("query", "").lower()
                for q in context.sql_queries
            ),
            # Dict/set iteration
            "for" in context.test_code and (".keys()" in context.test_code or ".items()" in context.test_code),
            "set(" in context.test_code,
            context.divergence_type == "ORDERING_DIFF",
        ]
        return any(indicators)

    def generate_fix(self, context: StrategyContext) -> Optional[StrategyFix]:
        """Generate ordering fix."""
        # Pattern: SQL without ORDER BY
        for query in context.sql_queries:
            sql = query.get("query", "").lower()
            if "select" in sql and "order by" not in sql:
                return StrategyFix(
                    diff="""-cursor.execute("SELECT * FROM users")
+cursor.execute("SELECT * FROM users ORDER BY id")""",
                    description="Add ORDER BY to SQL query for deterministic results",
                    confidence=0.90,
                    reasoning="SELECT without ORDER BY returns rows in arbitrary order",
                )

        # Pattern: Dict iteration
        if ".keys()" in context.test_code or ".items()" in context.test_code:
            return StrategyFix(
                diff="""-for key in data.keys():
+for key in sorted(data.keys()):""",
                description="Sort dictionary keys for deterministic iteration",
                confidence=0.85,
                reasoning="Dict iteration order is not guaranteed",
            )

        return None


class ResourceStrategy(BaseStrategy):
    """Strategy for resource-related flakiness.

    Addresses:
    - Hardcoded ports
    - File path conflicts
    - Temporary file issues
    - Resource cleanup
    """

    name = "resource"
    category = FlakinessCategory.RESOURCE
    description = "Fix resource conflicts and cleanup issues"
    priority = 7

    def applies_to(self, context: StrategyContext) -> bool:
        """Check for resource-related patterns."""
        import re

        indicators = [
            # Hardcoded ports
            re.search(r"port\s*=\s*\d{4,5}", context.test_code) is not None,
            # Hardcoded paths
            "/tmp/" in context.test_code,
            # File operations without cleanup
            "open(" in context.test_code and "finally" not in context.test_code,
            context.divergence_type == "RESOURCE_CONFLICT",
        ]
        return any(indicators)

    def generate_fix(self, context: StrategyContext) -> Optional[StrategyFix]:
        """Generate resource fix."""
        import re

        # Pattern: Hardcoded port
        port_match = re.search(r"port\s*=\s*(\d{4,5})", context.test_code)
        if port_match:
            return StrategyFix(
                diff=f"""-port = {port_match.group(1)}
+import socket
+def get_free_port():
+    with socket.socket() as s:
+        s.bind(('', 0))
+        return s.getsockname()[1]
+port = get_free_port()""",
                description="Use dynamic port instead of hardcoded value",
                confidence=0.90,
                reasoning="Hardcoded ports cause conflicts in parallel test runs",
            )

        # Pattern: Hardcoded /tmp path
        if "/tmp/" in context.test_code:
            return StrategyFix(
                diff="""-path = "/tmp/test_file.txt"
+import tempfile
+with tempfile.NamedTemporaryFile(delete=False) as f:
+    path = f.name""",
                description="Use tempfile for temporary paths",
                confidence=0.85,
                reasoning="Hardcoded paths cause conflicts between test runs",
            )

        return None


class StateStrategy(BaseStrategy):
    """Strategy for state-related flakiness.

    Addresses:
    - Global state mutation
    - Test pollution
    - Missing cleanup
    - Shared fixtures
    """

    name = "state"
    category = FlakinessCategory.STATE
    description = "Fix shared state and test isolation issues"
    priority = 8

    def applies_to(self, context: StrategyContext) -> bool:
        """Check for state-related patterns."""
        indicators = [
            # Global mutations
            "global " in context.test_code,
            # Missing cleanup
            "setUp" in context.test_code and "tearDown" not in context.test_code,
            # Class-level state
            "cls." in context.test_code,
            context.divergence_type == "STATE_POLLUTION",
        ]
        return any(indicators)

    def generate_fix(self, context: StrategyContext) -> Optional[StrategyFix]:
        """Generate state isolation fix."""
        # Pattern: Missing tearDown
        if "setUp" in context.test_code and "tearDown" not in context.test_code:
            return StrategyFix(
                diff="""+    def tearDown(self):
+        # Clean up any state created in setUp
+        super().tearDown()""",
                description="Add tearDown to clean up test state",
                confidence=0.75,
                reasoning="Missing cleanup causes state pollution between tests",
            )

        # Pattern: Global mutation
        if "global " in context.test_code:
            return StrategyFix(
                diff="""-global shared_data
-shared_data = {}
+# Use pytest fixtures for isolated state
+@pytest.fixture
+def test_data():
+    return {}""",
                description="Replace global state with pytest fixture",
                confidence=0.80,
                reasoning="Global state causes test pollution",
            )

        return None


class NetworkStrategy(BaseStrategy):
    """Strategy for network-related flakiness.

    Addresses:
    - External API calls without mocks
    - Missing retries
    - Network timeouts
    - DNS issues
    """

    name = "network"
    category = FlakinessCategory.NETWORK
    description = "Fix external service and network issues"
    priority = 9

    def applies_to(self, context: StrategyContext) -> bool:
        """Check for network-related patterns."""
        indicators = [
            # HTTP calls without mocks
            len(context.http_calls) > 0,
            "requests." in context.test_code,
            "httpx." in context.test_code,
            "aiohttp" in context.test_code,
            context.divergence_type in ("NETWORK_TIMEOUT", "HTTP_MISMATCH"),
        ]
        return any(indicators)

    def generate_fix(self, context: StrategyContext) -> Optional[StrategyFix]:
        """Generate network fix."""
        # Pattern: Unmocked HTTP calls
        if context.http_calls or "requests." in context.test_code:
            return StrategyFix(
                diff="""-response = requests.get("https://api.example.com/data")
+import responses
+
+@responses.activate
+def test_api_call():
+    responses.add(
+        responses.GET,
+        "https://api.example.com/data",
+        json={"data": "mocked"},
+        status=200,
+    )
+    response = requests.get("https://api.example.com/data")""",
                description="Mock external HTTP calls",
                confidence=0.90,
                reasoning="External APIs are unreliable and cause flaky tests",
            )

        return None


class ConcurrencyStrategy(BaseStrategy):
    """Strategy for concurrency-related flakiness.

    Addresses (expanded per E6/F-06):
    - Thread safety issues (thread without join)
    - Data races (shared mutable state in threaded code)
    - concurrent.futures Executor patterns
    - Async coordination (create_task without gather)
    - Lock contention
    """

    name = "concurrency"
    category = FlakinessCategory.CONCURRENCY
    description = "Fix thread safety and coordination issues"
    priority = 7

    def applies_to(self, context: StrategyContext) -> bool:
        """Check for concurrency-related patterns."""
        indicators = [
            "threading.Thread" in context.test_code,
            "multiprocessing" in context.test_code,
            "asyncio.gather" in context.test_code,
            ".start()" in context.test_code and ".join()" not in context.test_code,
            context.divergence_type == "RACE_CONDITION",
            # E6/F-06: Additional concurrency sub-patterns
            "ThreadPoolExecutor" in context.test_code,
            "ProcessPoolExecutor" in context.test_code,
            "concurrent.futures" in context.test_code,
            "asyncio.create_task" in context.test_code and "gather" not in context.test_code,
            "asyncio.ensure_future" in context.test_code,
        ]
        return any(indicators)

    def generate_fix(self, context: StrategyContext) -> Optional[StrategyFix]:
        """Generate concurrency fix."""
        # Pattern: Thread without join
        if ".start()" in context.test_code and ".join()" not in context.test_code:
            return StrategyFix(
                diff="""-thread.start()
-# ... more code ...
+thread.start()
+thread.join()  # Wait for thread to complete
+# ... more code ...""",
                description="Add thread.join() to wait for completion",
                confidence=0.85,
                reasoning="Not joining threads causes race conditions",
            )

        # E6/F-06: Pattern -- ThreadPoolExecutor without result collection
        if "ThreadPoolExecutor" in context.test_code or "ProcessPoolExecutor" in context.test_code:
            return StrategyFix(
                diff="""-with ThreadPoolExecutor() as executor:
-    futures = [executor.submit(task, arg) for arg in args]
+from concurrent.futures import as_completed
+with ThreadPoolExecutor() as executor:
+    futures = [executor.submit(task, arg) for arg in args]
+    for future in as_completed(futures):
+        future.result()  # Propagate exceptions, ensure completion""",
                description="Collect futures with as_completed() to ensure task completion",
                confidence=0.80,
                reasoning="Uncollected futures can cause race conditions and swallow exceptions",
            )

        # E6/F-06: Pattern -- asyncio.create_task without gather
        if "asyncio.create_task" in context.test_code and "gather" not in context.test_code:
            return StrategyFix(
                diff="""-task1 = asyncio.create_task(coro1())
-task2 = asyncio.create_task(coro2())
-# assertions here may race
+task1 = asyncio.create_task(coro1())
+task2 = asyncio.create_task(coro2())
+await asyncio.gather(task1, task2)  # Wait for all tasks
+# assertions here are safe""",
                description="Gather async tasks before asserting results",
                confidence=0.85,
                reasoning="Ungathered tasks may not complete before assertions",
            )

        return None


class FloatingPointStrategy(BaseStrategy):
    """Strategy for floating-point comparison flakiness.

    Addresses:
    - Direct float equality checks (assert x == 0.3)
    - Missing pytest.approx() usage
    - IEEE 754 rounding issues
    """

    name = "floating_point"
    category = FlakinessCategory.FLOATING_POINT
    description = "Fix floating-point comparison issues"
    priority = 7

    def applies_to(self, context: StrategyContext) -> bool:
        """Check for floating-point comparison patterns."""
        code = context.test_code
        indicators = [
            "== 0." in code and "approx" not in code,
            "assertEqual" in code and any(f"0.{d}" in code for d in "123456789"),
            "assert_almost_equal" not in code and "float" in (context.error_message or "").lower(),
            context.error_type == "AssertionError" and "!=" in (context.error_message or ""),
        ]
        return any(indicators)

    def generate_fix(self, context: StrategyContext) -> Optional[StrategyFix]:
        """Generate fix for floating-point comparisons."""
        import re
        code = context.test_code

        # Find direct float equality
        match = re.search(r'assert\s+(\w+)\s*==\s*([\d.]+)', code)
        if match:
            var, val = match.group(1), match.group(2)
            return StrategyFix(
                diff=f"""-assert {var} == {val}
+assert {var} == pytest.approx({val})""",
                description="Use pytest.approx() for floating-point comparison",
                confidence=0.9,
                reasoning="Direct float equality fails due to IEEE 754 rounding",
            )

        return None


class TimezoneStrategy(BaseStrategy):
    """Strategy for timezone-related flakiness.

    Addresses:
    - datetime.now() without timezone
    - DST transition issues
    - Timezone-naive comparisons
    """

    name = "timezone"
    category = FlakinessCategory.TIMING
    description = "Fix timezone and DST-related issues"
    priority = 6

    def applies_to(self, context: StrategyContext) -> bool:
        """Check for timezone-related patterns."""
        code = context.test_code
        indicators = [
            "datetime.now()" in code and "timezone" not in code and "tz=" not in code,
            "datetime.utcnow()" in code,
            "strftime" in code and "timezone" not in code,
            context.error_type == "AssertionError" and "hour" in (context.error_message or "").lower(),
        ]
        return any(indicators)

    def generate_fix(self, context: StrategyContext) -> Optional[StrategyFix]:
        """Generate fix for timezone issues."""
        code = context.test_code

        if "datetime.now()" in code and "timezone" not in code:
            return StrategyFix(
                diff="""-from datetime import datetime
-now = datetime.now()
+from datetime import datetime, timezone
+now = datetime.now(timezone.utc)""",
                description="Use timezone-aware datetime.now()",
                confidence=0.85,
                reasoning="datetime.now() without timezone causes DST and TZ-dependent failures",
            )

        if "datetime.utcnow()" in code:
            return StrategyFix(
                diff="""-now = datetime.utcnow()
+now = datetime.now(timezone.utc)""",
                description="Replace deprecated utcnow() with timezone-aware now()",
                confidence=0.9,
                reasoning="utcnow() is deprecated and returns naive datetime",
            )

        return None


class MockLeakageStrategy(BaseStrategy):
    """Strategy for mock leakage flakiness.

    Addresses:
    - @patch without proper cleanup
    - Missing mock.patch.stopall()
    - Mock side effects leaking between tests
    """

    name = "mock_leakage"
    category = FlakinessCategory.STATE
    description = "Fix mock leakage between tests"
    priority = 7

    def applies_to(self, context: StrategyContext) -> bool:
        """Check for mock leakage patterns."""
        code = context.test_code
        indicators = [
            "@patch" in code and "def tearDown" not in code and "autouse" not in code,
            "mock.patch(" in code and "stop()" not in code and "with " not in code,
            "side_effect" in code and "reset_mock" not in code,
            context.divergence_type == "STATE_LEAK" and "mock" in code.lower(),
        ]
        return any(indicators)

    def generate_fix(self, context: StrategyContext) -> Optional[StrategyFix]:
        """Generate fix for mock leakage."""
        code = context.test_code

        if "mock.patch(" in code and "with " not in code and "stop()" not in code:
            return StrategyFix(
                diff="""-patcher = mock.patch('module.func')
-mock_func = patcher.start()
+patcher = mock.patch('module.func')
+mock_func = patcher.start()
+self.addCleanup(patcher.stop)""",
                description="Add cleanup for mock patcher to prevent leakage",
                confidence=0.85,
                reasoning="Mock patches without stop() leak between tests",
            )

        if "@patch" in code and "autouse" not in code:
            return StrategyFix(
                diff="""-@patch('module.func')
-def test_something(self, mock_func):
+@patch('module.func')
+def test_something(self, mock_func):
+    mock_func.reset_mock()""",
                description="Reset mock at start of test to prevent state leakage",
                confidence=0.7,
                reasoning="Mocks can retain state from previous test invocations",
            )

        return None


class SnapshotStrategy(BaseStrategy):
    """Strategy for stale snapshot test flakiness.

    Addresses:
    - Stale snapshot files
    - Dynamic content in snapshots (timestamps, UUIDs)
    - Platform-dependent snapshot differences
    """

    name = "snapshot"
    category = FlakinessCategory.STATE
    description = "Fix stale or unstable snapshot tests"
    priority = 5

    def applies_to(self, context: StrategyContext) -> bool:
        """Check for snapshot-related patterns."""
        code = context.test_code
        msg = (context.error_message or "").lower()
        indicators = [
            "snapshot" in code.lower() and "assert" in code.lower(),
            "toMatchSnapshot" in code,
            "assert_match_snapshot" in code,
            "snapshot" in msg and ("mismatch" in msg or "not match" in msg),
        ]
        return any(indicators)

    def generate_fix(self, context: StrategyContext) -> Optional[StrategyFix]:
        """Generate fix for snapshot issues."""
        code = context.test_code

        if "toMatchSnapshot" in code:
            return StrategyFix(
                diff="""-expect(result).toMatchSnapshot();
+expect(result).toMatchSnapshot({
+  createdAt: expect.any(String),
+  id: expect.any(String),
+});""",
                description="Use property matchers for dynamic fields in snapshot",
                confidence=0.75,
                reasoning="Dynamic fields like timestamps and IDs cause snapshot flakiness",
            )

        return None


class EnvironmentStrategy(BaseStrategy):
    """Strategy for environment-dependent flakiness.

    Addresses:
    - CI vs local differences
    - Docker/container-specific behavior
    - Locale-dependent formatting
    """

    name = "environment"
    category = FlakinessCategory.ENVIRONMENT
    description = "Fix environment-dependent test flakiness"
    priority = 6

    def applies_to(self, context: StrategyContext) -> bool:
        """Check for environment-dependent patterns."""
        code = context.test_code
        indicators = [
            'os.environ["CI"]' in code,
            "os.getenv" in code and "CI" in code,
            "process.env.CI" in code,
            "locale.setlocale" in code,
            "strftime" in code and "locale" not in code.lower(),
            "/proc/" in code,
            "cgroup" in code,
            "DOCKER_HOST" in code,
        ]
        return any(indicators)

    def generate_fix(self, context: StrategyContext) -> Optional[StrategyFix]:
        """Generate fix for environment-dependent tests."""
        import re
        code = context.test_code

        # CI environment access without default
        match = re.search(r'os\.environ\["(CI|GITHUB_ACTIONS|JENKINS)"\]', code)
        if match:
            var = match.group(1)
            return StrategyFix(
                diff=f"""-os.environ["{var}"]
+os.environ.get("{var}", "false")""",
                description=f"Use safe environment variable access for {var}",
                confidence=0.8,
                reasoning="CI environment variables may not exist in all environments",
            )

        # Locale-dependent code without explicit locale
        if "strftime" in code and "setlocale" not in code:
            return StrategyFix(
                diff="""+import locale
+locale.setlocale(locale.LC_ALL, "C")  # Use deterministic locale""",
                description="Set explicit locale for deterministic formatting",
                confidence=0.7,
                reasoning="System locale varies between CI and local environments",
            )

        return None


class DatabaseIsolationStrategy(BaseStrategy):
    """Strategy for database-related flakiness (E6/F-07).

    Addresses:
    - Shared database state between tests
    - Missing transaction rollback in fixtures
    - ORM session reuse across tests
    - Stale data from previous test runs
    """

    name = "database_isolation"
    category = FlakinessCategory.STATE
    description = "Fix shared database state and missing test isolation"
    priority = 8

    def applies_to(self, context: StrategyContext) -> bool:
        """Check for database isolation patterns."""
        code = context.test_code
        indicators = [
            # SQL queries without transaction isolation
            any(
                "select" in q.get("query", "").lower()
                for q in context.sql_queries
            ),
            # ORM session patterns without per-test isolation
            "session.query" in code and "rollback" not in code,
            "Session(" in code and "rollback" not in code,
            # Database fixtures without cleanup
            "db_session" in code and "rollback" not in code,
            # Shared database state divergence
            context.divergence_type == "DB_STATE_POLLUTION",
            # Django without proper test case
            "Model.objects" in code and "TransactionTestCase" not in code,
        ]
        return any(indicators)

    def generate_fix(self, context: StrategyContext) -> Optional[StrategyFix]:
        """Generate database isolation fix."""
        code = context.test_code

        if "session" in code.lower() and "rollback" not in code:
            return StrategyFix(
                diff="""+@pytest.fixture(autouse=True)
+def db_cleanup(db_session):
+    \"\"\"Roll back database state after each test.\"\"\"
+    yield
+    db_session.rollback()""",
                description="Add autouse fixture to rollback database after each test",
                confidence=0.80,
                reasoning="Shared database state causes order-dependent test failures",
            )

        if context.sql_queries:
            return StrategyFix(
                diff="""+@pytest.fixture(autouse=True)
+def transactional_test(db_connection):
+    \"\"\"Wrap each test in a transaction that is rolled back.\"\"\"
+    transaction = db_connection.begin()
+    yield
+    transaction.rollback()""",
                description="Wrap tests in rolled-back transactions for isolation",
                confidence=0.85,
                reasoning="Per-test transactions prevent cross-test database pollution",
            )

        return None


class RandomnessStrategy(BaseStrategy):
    """Strategy for randomness-related flakiness (E6/F-12).

    Addresses:
    - Unseeded random number generators
    - Non-deterministic sampling
    - UUID-based assertions
    """

    name = "randomness"
    category = FlakinessCategory.RANDOMNESS
    description = "Fix non-deterministic randomness in tests"
    priority = 6

    def applies_to(self, context: StrategyContext) -> bool:
        """Check for randomness-related patterns."""
        code = context.test_code
        indicators = [
            "random." in code and "random.seed" not in code,
            "np.random." in code and "np.random.seed" not in code,
            "Math.random" in code,
            "uuid4()" in code and "mock" not in code.lower(),
            "shuffle(" in code and "seed" not in code,
        ]
        return any(indicators)

    def generate_fix(self, context: StrategyContext) -> Optional[StrategyFix]:
        """Generate randomness fix."""
        code = context.test_code

        if "random." in code and "random.seed" not in code:
            return StrategyFix(
                diff="""+import random
+random.seed(42)  # Fixed seed for reproducibility""",
                description="Add fixed random seed for deterministic test execution",
                confidence=0.90,
                reasoning="Unseeded random produces non-deterministic results",
            )

        if "np.random." in code and "np.random.seed" not in code:
            return StrategyFix(
                diff="""+import numpy as np
+np.random.seed(42)  # Fixed seed for reproducibility""",
                description="Add fixed numpy random seed",
                confidence=0.90,
                reasoning="Unseeded numpy random produces non-deterministic results",
            )

        return None


class PlatformStrategy(BaseStrategy):
    """Strategy for platform-dependent flakiness (E6/F-12).

    Addresses:
    - Hardcoded path separators (/ vs \\)
    - Platform-specific line endings
    - Hardcoded temp directory paths (/tmp)
    - OS-specific executable extensions
    """

    name = "platform"
    category = FlakinessCategory.PLATFORM
    description = "Fix platform-dependent test assumptions"
    priority = 5

    def applies_to(self, context: StrategyContext) -> bool:
        """Check for platform-dependent patterns."""
        import re as _re
        code = context.test_code
        indicators = [
            # Hardcoded Unix paths in assertions
            _re.search(r'assert.*["\']/.+/.*["\']', code) is not None,
            # Hardcoded /tmp
            '"/tmp/' in code or "'/tmp/" in code,
            # Line ending assumptions
            ".split('\\n')" in code and ".splitlines()" not in code,
            # Windows path in assertions
            _re.search(r'assert.*["\']C:\\', code) is not None,
        ]
        return any(indicators)

    def generate_fix(self, context: StrategyContext) -> Optional[StrategyFix]:
        """Generate platform fix."""
        code = context.test_code

        if '"/tmp/' in code or "'/tmp/" in code:
            return StrategyFix(
                diff="""-path = "/tmp/test_output.txt"
+import tempfile
+path = os.path.join(tempfile.gettempdir(), "test_output.txt")""",
                description="Use tempfile.gettempdir() instead of hardcoded /tmp",
                confidence=0.90,
                reasoning="Hardcoded /tmp fails on Windows and some CI environments",
            )

        if ".split('\\n')" in code:
            return StrategyFix(
                diff="""-lines = content.split('\\n')
+lines = content.splitlines()""",
                description="Use .splitlines() for platform-agnostic line splitting",
                confidence=0.90,
                reasoning=".split('\\n') misses \\r\\n on Windows",
            )

        return None


# Registry of all strategies
ALL_STRATEGIES: List[BaseStrategy] = [
    TimingStrategy(),
    OrderingStrategy(),
    ResourceStrategy(),
    StateStrategy(),
    NetworkStrategy(),
    ConcurrencyStrategy(),
    FloatingPointStrategy(),
    TimezoneStrategy(),
    MockLeakageStrategy(),
    SnapshotStrategy(),
    EnvironmentStrategy(),
    DatabaseIsolationStrategy(),
    RandomnessStrategy(),
    PlatformStrategy(),
]


def get_strategies_for_category(category: FlakinessCategory) -> List[BaseStrategy]:
    """Get all strategies for a flakiness category."""
    return [s for s in ALL_STRATEGIES if s.category == category]


def get_applicable_strategies(context: StrategyContext) -> List[BaseStrategy]:
    """Get all strategies that apply to the given context."""
    applicable = [s for s in ALL_STRATEGIES if s.applies_to(context)]
    return sorted(applicable, key=lambda s: s.priority, reverse=True)


def generate_all_fixes(context: StrategyContext) -> List[StrategyFix]:
    """Generate fixes from all applicable strategies."""
    fixes = []
    for strategy in get_applicable_strategies(context):
        fix = strategy.generate_fix(context)
        if fix:
            fixes.append(fix)
    return fixes
