"""
╔════════════════════════════════════════════════════════════════════════════╗
║                    SHARED PYTEST FIXTURES - CONFTEST.PY                    ║
║                                                                            ║
║  This file is AUTO-DISCOVERED by pytest and runs before all tests.         ║
║  All fixtures defined here are available to ALL test files without         ║
║  explicit imports.                                                         ║
║                                                                            ║
║  Generated by: GitHub Copilot (Claude 3.5)                                 ║
║  Model: claude-3-5-sonnet-20241022                                         ║
║  Confidence in core logic: ~90%                                            ║
║                                                                            ║
║  ⚠️  DISCLAIMER:                                                           ║
║  This conftest.py provides reusable fixtures for the Multiplied library    ║
║  test suite. Fixtures are:                                                 ║
║    1. Shared DataFrame fixtures (various sizes and structures)             ║
║    2. Temporary file/directory fixtures                                    ║
║    3. Algorithm fixtures (internal module dependencies)                    ║
║    4. Cleanup utilities                                                    ║
║                                                                            ║
║  Expected accuracy: ~90%                                                   ║
║  Core fixture logic is solid. Main uncertainty:                            ║
║    - Whether DataFrame structure matches ALL actual use cases              ║
║    - Scope choices (function vs module) may need tuning                    ║
║    - Algorithm generation may differ from production data                  ║
║                                                                            ║
║  HOW TO USE IN OTHER TESTS:                                                ║
║  Simply reference fixture by name in test parameters:                      ║
║                                                                            ║
║    def test_my_function(sample_heatmap_dataframe):                         ║
║        # sample_heatmap_dataframe is automatically created                 ║
║        assert len(sample_heatmap_dataframe) > 0                            ║
║                                                                            ║
╚════════════════════════════════════════════════════════════════════════════╝
"""

import pytest
import pandas as pd
import matplotlib.pyplot as plt

# Import Multiplied library
import multiplied as mp


# ============================================================================
# SECTION 1: TEMPORARY FILE/DIRECTORY FIXTURES
# Use these when tests need to write output files
# ============================================================================


@pytest.fixture
def temp_svg_output(tmp_path):
    """
    FIXTURE: Temporary SVG file path

    SCOPE: function (creates new temp file for each test)

    PURPOSE:
        Provides a temporary file path for SVG output.
        File is automatically cleaned up after test completes.

    USAGE:
        def test_something(temp_svg_output):
            mp.df_global_heatmap(str(temp_svg_output), "Title", df)
            assert temp_svg_output.exists()

    RETURNS:
        Path object pointing to temp .svg file

    AI CONFIDENCE: ~99%
        This is standard pytest practice. Guaranteed to work.

    WHAT TO VERIFY:
        ✓ You can write to this path
        ✓ File is cleaned up after test
        ✓ Path format works on your OS
    """
    return tmp_path / "test_output.svg"


@pytest.fixture
def temp_png_output(tmp_path):
    """
    FIXTURE: Temporary PNG file path

    SCOPE: function

    PURPOSE:
        Provides a temporary file path for PNG output.
        Use when testing PNG export functionality.

    USAGE:
        def test_png_export(temp_png_output):
            mp.df_global_heatmap(str(temp_png_output), "Title", df)
            assert temp_png_output.exists()

    RETURNS:
        Path object pointing to temp .png file

    AI CONFIDENCE: ~99%
    """
    return tmp_path / "test_output.png"


@pytest.fixture
def temp_jpg_output(tmp_path):
    """
    FIXTURE: Temporary JPG file path

    SCOPE: function

    PURPOSE:
        Provides a temporary file path for JPG output.
        Use when testing JPG export functionality.

    RETURNS:
        Path object pointing to temp .jpg file

    AI CONFIDENCE: ~99%
    """
    return tmp_path / "test_output.jpg"


@pytest.fixture
def temp_output_dir(tmp_path):
    """
    FIXTURE: Temporary directory for multiple output files

    SCOPE: function

    PURPOSE:
        Provides a temp directory when a test needs to create
        multiple output files (not just one).

    USAGE:
        def test_multiple_outputs(temp_output_dir):
            file1 = temp_output_dir / "heatmap1.svg"
            file2 = temp_output_dir / "heatmap2.svg"
            mp.df_global_heatmap(str(file1), "Title1", df1)
            mp.df_global_heatmap(str(file2), "Title2", df2)

    RETURNS:
        Path object pointing to empty temp directory

    AI CONFIDENCE: ~99%
    """
    return tmp_path


# ============================================================================
# SECTION 2: BASIC DATAFRAME FIXTURES
# Use these to create sample test data with various structures
# ============================================================================

# @pytest.fixture
# def sample_heatmap_dataframe():
#     """
#     FIXTURE: Minimal valid heatmap DataFrame

#     SCOPE: function

#     PURPOSE:
#         Creates a small DataFrame with proper column format for heatmap testing.
#         This is the most basic fixture - use when you just need some valid data.

#     STRUCTURE:
#         - 2 stages (s0, s1)
#         - 2 partial product matrices per stage (p0, p1)
#         - 8 bits per matrix (b0-b7)
#         - 5 rows of data
#         - Columns named: s{stage}_p{ppm}_b{bit}

#     EXAMPLE OUTPUT:
#         Index  s0_p0_b0  s0_p0_b1  s0_p1_b0  ... s1_p1_b7
#         0           0         1         0   ...        1
#         1           1         0         1   ...        0
#         2           0         1         0   ...        1
#         3           1         0         1   ...        0
#         4           1         1         0   ...        1

#     USAGE:
#         def test_heatmap(sample_heatmap_dataframe):
#             assert len(sample_heatmap_dataframe) == 5
#             mp.df_global_heatmap("test.svg", "Title", sample_heatmap_dataframe)

#     RETURNS:
#         pd.DataFrame with proper column structure

#     AI CONFIDENCE: ~95%
#         Column structure matches function's parsing logic.
#         However, verify this matches YOUR actual DataFrame format.

#     WHAT TO VERIFY:
#         ⚠️  CRITICAL: Check if your actual data uses this naming convention
#         - Does your data have 2 stages? (adjust num_stages if not)
#         - Does your data have 2 PPMs per stage? (adjust num_ppms if not)
#         - Are bits 8-bit wide? (adjust num_bits if not)
#     """
#     column_data = {}

#     # Configuration - ADJUST THESE if your data is different
#     num_stages = 2     # Number of stages in algorithm
#     num_ppms = 4       # PPMs per stage
#     num_bits = 8      # Bit width
#     num_rows = 5       # Number of test data rows

#     # Create columns in format: s{stage}_p{ppm}_b{bit}
#     for stage in range(num_stages):
#         for ppm in range(num_ppms):
#             for bit in range(num_bits):
#                 col_name = f"s{stage}_p{ppm}_b{bit}"
#                 # Fill with alternating 0/1 values
#                 column_data[col_name] = [i % 2 for i in range(num_rows)]

#     return pd.DataFrame(column_data)


@pytest.fixture
def large_heatmap_dataframe():
    """
    FIXTURE: Larger DataFrame for stress testing

    SCOPE: function

    PURPOSE:
        Creates a larger DataFrame to test performance and stability
        with realistic data sizes. Use when testing scalability.

    STRUCTURE:
        - 4 stages
        - 3 PPMs per stage
        - 16 bits per matrix
        - 100 rows of data

    USAGE:
        def test_heatmap_with_large_data(large_heatmap_dataframe):
            mp.df_global_heatmap("test.svg", "Title", large_heatmap_dataframe)
            # Performance testing

    RETURNS:
        pd.DataFrame with larger structure

    AI CONFIDENCE: ~90%
        Same structure as sample, just larger.
        May take longer to generate/process.
    """
    column_data = {}

    num_stages = 4
    num_ppms = 8
    num_bits = 16
    num_rows = 500

    for stage in range(num_stages):
        for ppm in range(num_ppms):
            for bit in range(num_bits):
                col_name = f"s{stage}_p{ppm}_b{bit}"
                # Fill with alternating 0/1 values
                column_data[col_name] = [i % 2 for i in range(num_rows)]

    return pd.DataFrame(column_data)


@pytest.fixture
def minimal_heatmap_dataframe():
    """
    FIXTURE: Smallest possible valid DataFrame

    SCOPE: function

    PURPOSE:
        Creates the absolute minimum DataFrame that should work.
        Use for edge case testing.

    STRUCTURE:
        - 1 stage
        - 1 PPM
        - 4 bits
        - 1 row of data

    USAGE:
        def test_edge_case_minimal_data(minimal_heatmap_dataframe):
            mp.df_global_heatmap("test.svg", "Title", minimal_heatmap_dataframe)

    RETURNS:
        Minimal valid pd.DataFrame

    AI CONFIDENCE: ~85%
        This should work, but function may have minimum size requirements.
        Verify it doesn't crash or produce invalid output.
    """
    column_data = {}

    # Absolute minimum
    num_stages = 1
    num_ppms = 4
    num_bits = 8
    # num_rows = 1 -- implied

    for stage in range(num_stages):
        for ppm in range(num_ppms):
            for bit in range(num_bits):
                col_name = f"s{stage}_p{ppm}_b{bit}"
                column_data[col_name] = [1]

    return pd.DataFrame(column_data)


# ============================================================================
# SECTION 3: REAL DATA FIXTURES
# These generate actual algorithm data using Multiplied's core functions
# ============================================================================


@pytest.fixture
def basic_algorithm():
    """
    FIXTURE: Basic 4-bit Algorithm object

    SCOPE: function

    PURPOSE:
        Creates a real Algorithm object by calling Multiplied's internal
        functions. Use when testing against actual algorithm output.

    STRUCTURE:
        - 4-bit multiplication algorithm
        - Auto-resolved stages
        - Ready for execution

    USAGE:
        def test_with_real_algorithm(basic_algorithm):
            result = basic_algorithm.exec(5, 3)
            assert result is not None

    RETURNS:
        mp.Algorithm instance

    AI CONFIDENCE: ~90%
        Algorithm generation is deterministic.
        May vary slightly between Multiplied versions.

    WHAT TO VERIFY:
        - Algorithm creates without errors
        - Algorithm can execute (basic smoke test)
    """
    # Create algorithm
    alg = mp.Algorithm(4)

    # Auto-resolve stages (generate templates)
    alg.auto_resolve_stage(recursive=True)

    return alg


@pytest.fixture
def real_truth_dataframe_small(basic_algorithm):
    """
    FIXTURE: Real DataFrame from algorithm truth table (small)

    SCOPE: function

    PURPOSE:
        Generates actual DataFrame using Multiplied's truth_dataframe()
        function. This is real data, not synthetic, so tests are more
        realistic.

    GENERATION:
        1. Creates basic 4-bit algorithm
        2. Generates truth scope (1-15 range)
        3. Creates truth dataframe from algorithm

    USAGE:
        def test_with_real_data(real_truth_dataframe_small):
            mp.df_global_heatmap("test.svg", "Title", real_truth_dataframe_small)
            # Test with actual algorithm output

    RETURNS:
        pd.DataFrame generated by mp.truth_dataframe()

    AI CONFIDENCE: ~85%
        This should work reliably, but algorithm changes could affect it.
        Takes time to generate, so use sparingly.

    SCOPE NOTE:
        Using function scope (not module) because:
        - Tests shouldn't share data (isolation principle)
        - Generation is fast enough for function scope
        - Reduces coupling between tests

    WHAT TO VERIFY:
        - DataFrame structure matches expectations
        - All required columns present
        - No NaN or null values
    """
    # Generate truth scope (domain and range)
    domain = (1, 15)  # Input values
    range_ = (1, 255)  # Output values
    scope = mp.truth_scope(domain, range_)

    # Generate truth dataframe
    df = mp.truth_dataframe(scope, basic_algorithm)

    return df


@pytest.fixture
def real_truth_dataframe_medium():
    """
    FIXTURE: Real DataFrame from medium-sized algorithm

    SCOPE: function

    PURPOSE:
        Like real_truth_dataframe_small but with larger algorithm (8-bit).
        Use when testing with more realistic data sizes.

    STRUCTURE:
        - 8-bit multiplication algorithm
        - Larger truth table
        - Takes longer to generate

    USAGE:
        def test_performance(real_truth_dataframe_medium):
            # Test with medium dataset
            mp.df_global_heatmap("test.svg", "Title", real_truth_dataframe_medium)

    RETURNS:
        Larger pd.DataFrame

    AI CONFIDENCE: ~85%
        Same generation as small, just bigger.
        May be slow (several seconds to generate).

    ⚠️  WARNING:
        This fixture is expensive (time/resources) to generate.
        Don't overuse - only when testing specifically with larger data.
    """
    # Create larger algorithm
    alg = mp.Algorithm(8)
    alg.auto_resolve_stage(recursive=True)

    # Generate smaller scope to keep runtime reasonable
    domain = (1, 31)  # Smaller range for 8-bit
    range_ = (1, 65535)
    scope = mp.truth_scope(domain, range_)

    df = mp.truth_dataframe(scope, alg)

    return df


# ============================================================================
# SECTION 4: PARAMETRIZED FIXTURES
# Create multiple data variants automatically
# ============================================================================


@pytest.fixture(params=[4, 8])
def algorithm_of_varying_widths(request):
    """
    FIXTURE: Algorithm with parametrized bit widths

    SCOPE: function

    PURPOSE:
        Automatically creates tests with multiple algorithm sizes.
        Single test using this fixture will run 3 times (once per param).

    PARAMETERS:
        - 4-bit algorithm
        - 8-bit algorithm

    USAGE:
        def test_heatmap_all_widths(algorithm_of_varying_widths):
            # This test runs 3 times automatically
            alg = algorithm_of_varying_widths
            scope = mp.truth_scope((1, 5), (1, 255))
            df = mp.truth_dataframe(scope, alg)
            mp.df_global_heatmap("test.svg", "Title", df)

    RETURNS:
        mp.Algorithm with varying widths

    PYTEST BEHAVIOR:
        If a test uses this fixture, pytest will:
        1. Create 4-bit algorithm, run test
        2. Create 8-bit algorithm, run test

        Test output will show: test_name[4], test_name[8]

    AI CONFIDENCE: ~90%
        Parametrization is standard pytest. Works reliably.

    EXAMPLE TEST OUTPUT:
        test_heatmap_all_widths[4] PASSED
        test_heatmap_all_widths[8] PASSED
    """
    bit_width = request.param

    alg = mp.Algorithm(bit_width)
    alg.auto_resolve_stage(recursive=True)

    return alg


@pytest.fixture(
    params=[
        {"dark": False, "title": "Light Theme"},
        {"dark": True, "title": "Dark Theme"},
    ]
)
def heatmap_theme_variants(request):
    """
    FIXTURE: Theme parameter variants

    SCOPE: function

    PURPOSE:
        Tests both light and dark theme variants automatically.
        Similar to algorithm_of_varying_widths but for theme testing.

    USAGE:
        def test_both_themes(heatmap_theme_variants, sample_heatmap_dataframe):
            config = heatmap_theme_variants
            mp.df_global_heatmap(
                "test.svg",
                config["title"],
                sample_heatmap_dataframe,
                dark=config["dark"]
            )

    RETURNS:
        dict with "dark" (bool) and "title" (str)

    PYTEST BEHAVIOR:
        Single test runs twice:
        test_name[light] - with dark=False
        test_name[dark] - with dark=True

    AI CONFIDENCE: ~95%
        Standard parametrization pattern.
    """
    return request.param


# ============================================================================
# SECTION 5: UTILITY FIXTURES & HOOKS
# Setup/teardown and utility functions
# ============================================================================


@pytest.fixture(autouse=True)
def cleanup_matplotlib():
    """
    FIXTURE: Clean up matplotlib state between tests

    SCOPE: function

    PURPOSE:
        Matplotlib maintains internal state (figures, styles, etc.).
        This fixture ensures tests don't interfere with each other.

    BEHAVIOR:
        - Runs BEFORE each test (setup)
        - Clears matplotlib figures
        - Resets matplotlib style
        - Runs AFTER each test (cleanup)
        - Closes any open figures

    ⚠️  autouse=True means this runs automatically, no need to request it

    WHY IT MATTERS:
        Without this:
        - Test 1 creates figure
        - Test 2 inherits figure from Test 1
        - Tests interfere with each other (flaky)

        With this:
        - Each test starts fresh
        - No interference

    AI CONFIDENCE: ~95%
        Standard matplotlib testing practice.

    WHAT IT DOES:
        Before test:
        - Clears all figures
        - Resets to default style

        After test:
        - Closes remaining figures
        - Clears figure cache
    """
    # SETUP: Run before test

    # Close any existing figures
    plt.close("all")

    # Reset to default style
    plt.rcdefaults()

    # Yield control to test
    yield

    # CLEANUP: Run after test
    # Close any figures test created
    plt.close("all")

    # Reset style again
    plt.rcdefaults()


# ============================================================================
# SECTION 6: PYTEST HOOKS
# Configure pytest behavior globally
# ============================================================================


def pytest_configure(config):
    """
    HOOK: Runs once at start of test session

    PURPOSE:
        Configure pytest-wide settings before any tests run.

    USES:
        - Register custom markers
        - Set test paths
        - Configure output

    AI CONFIDENCE: ~90%
        Standard pytest hook.
    """
    # Register custom markers (optional, but useful)
    config.addinivalue_line(
        "markers", "slow: marks tests as slow (deselect with '-m \"not slow\"')"
    )

    config.addinivalue_line("markers", "integration: marks tests as integration tests")

    config.addinivalue_line("markers", "visual: marks tests that verify visual output")


# ============================================================================
# SECTION 7: FIXTURE DOCUMENTATION & REFERENCE
# ============================================================================

"""
═══════════════════════════════════════════════════════════════════════════════
FIXTURES QUICK REFERENCE
═══════════════════════════════════════════════════════════════════════════════

TEMPORARY FILES (for output):
  - temp_svg_output .............. SVG file path
  - temp_png_output .............. PNG file path
  - temp_jpg_output .............. JPG file path
  - temp_output_dir .............. Temp directory

SYNTHETIC DATAFRAMES:
  - sample_heatmap_dataframe ...... Small basic DataFrame (5x32 columns)
  - large_heatmap_dataframe ....... Large DataFrame (100x192 columns)
  - minimal_heatmap_dataframe ..... Minimal DataFrame (1x4 columns)

REAL DATA (from Multiplied):
  - basic_algorithm .............. 4-bit Algorithm object
  - real_truth_dataframe_small ... Small truth table (4-bit)
  - real_truth_dataframe_medium .. Medium truth table (8-bit)

PARAMETRIZED (create multiple test variants):
  - algorithm_of_varying_widths .. 2/4/8-bit algorithms (runs test 3x)
  - heatmap_theme_variants ....... Light/dark themes (runs test 2x)

AUTOMATIC (run without requesting):
  - cleanup_matplotlib ........... Clears matplotlib state

═══════════════════════════════════════════════════════════════════════════════
HOW TO USE IN TESTS
═══════════════════════════════════════════════════════════════════════════════

Example 1: Simple test with basic data
  def test_basic(sample_heatmap_dataframe, temp_svg_output):
      mp.df_global_heatmap(str(temp_svg_output), "Title", sample_heatmap_dataframe)
      assert temp_svg_output.exists()

Example 2: Test with parametrized algorithm
  def test_all_widths(algorithm_of_varying_widths):
      # Runs 3 times with different algorithms
      assert algorithm_of_varying_widths is not None

Example 3: Real data test
  def test_real_data(real_truth_dataframe_small, temp_svg_output):
      mp.df_global_heatmap(str(temp_svg_output), "Title", real_truth_dataframe_small)
      assert temp_svg_output.exists()

Example 4: Multiple files
  def test_multiple_outputs(algorithm_of_varying_widths, temp_output_dir):
      for i in range(3):
          path = temp_output_dir / f"heatmap_{i}.svg"
          # Use path for output
          assert path.parent == temp_output_dir

═══════════════════════════════════════════════════════════════════════════════
FIXTURE SCOPES
═══════════════════════════════════════════════════════════════════════════════

function (default):
  ✓ Created before each test
  ✓ Destroyed after each test
  ✓ Each test gets fresh data
  ✓ Safest, most isolated
  ✗ Slower for expensive operations

  Use for: Most fixtures

module:
  ✓ Created once per test file
  ✓ Shared across all tests in file
  ✓ Faster for expensive operations
  ✗ Tests may interfere

  Use for: Large DataFrames that are slow to generate and read-only

session:
  ✓ Created once for entire test run
  ✓ Shared across all tests
  ✓ Fastest for one-time setup
  ✗ Tests definitely interfere

  Use for: Database connections, static files

═══════════════════════════════════════════════════════════════════════════════
COMMON FIXTURE COMBINATIONS
═════════════════════════════════════════��═════════════════════════════════════

Simplest test (file output test):
  def test_creates_file(sample_heatmap_dataframe, temp_svg_output):
      ...

Visual/structure test (no file needed):
  def test_svg_structure(sample_heatmap_dataframe):
      df = sample_heatmap_dataframe
      # Don't need file output, test in-memory
      ...

Integration test (real data):
  def test_real_world(real_truth_dataframe_small, temp_svg_output):
      mp.df_global_heatmap(str(temp_svg_output), "Title", real_truth_dataframe_small)
      ...

Performance test (large data):
  def test_performance(large_heatmap_dataframe, temp_svg_output):
      import time
      start = time.time()
      mp.df_global_heatmap(str(temp_svg_output), "Title", large_heatmap_dataframe)
      elapsed = time.time() - start
      assert elapsed < 10  # Should complete in < 10 seconds

Parametrized test (multiple variants):
  def test_all_algorithms(algorithm_of_varying_widths, temp_svg_output):
      scope = mp.truth_scope((1, 15), (1, 255))
      df = mp.truth_dataframe(scope, algorithm_of_varying_widths)
      mp.df_global_heatmap(str(temp_svg_output), "Title", df)

═══════════════════════════════════════════════════════════════════════════════
TROUBLESHOOTING
═══════════════════════════════════════════════════════════════════════════════

Q: "Fixture 'sample_heatmap_dataframe' not found"
A: Check:
   1. conftest.py is in tests/ directory
   2. __init__.py exists in tests/ directory (create if missing)
   3. pytest discovers it: pytest --fixtures | grep sample

Q: Tests pass individually but fail together
A: Probably matplotlib state issue. cleanup_matplotlib should handle.
   Try: pytest tests/ --forked (uses pytest-forked for isolation)

Q: Fixture generation is slow
A: Change scope to module (shared) if safe:
   @pytest.fixture(scope="module")
   def expensive_fixture():
       ...

Q: Need different fixture data for one test
A: Either:
   1. Create a new fixture variant
   2. Use monkeypatch to modify fixture
   3. Create data inline in test

═══════════════════════════════════════════════════════════════════════════════
"""
