import numpy as np
import pytest
import yaml
from astropy.table import Table
from qualpipe.core.metric import Metric
from traitlets.config import Config


@pytest.fixture()
def setup_metric():
    metric_config_yaml = """
    Metric:
      name: "test_metric"
      input_source: "dummy_data_file.h5:"
      table_column: "data"
      array_element: DUMMY
      telescope_specific: False
      descriptor: PercentageDescriptor
      descriptor_column: percentage
      criteria:
        - RangeCriterion:
            min_value: 0
            max_value: 100
    """
    metric_config_dict = yaml.safe_load(metric_config_yaml)
    metric_config = Config(metric_config_dict)
    metric = Metric(config=metric_config)
    return metric


@pytest.mark.verifies_usecase("UC-140-1.2.1")
def test_metric_descriptor(setup_metric, sample_table):
    """Test that descriptor returns a table with statistics."""
    metric = setup_metric
    result_table = metric._descriptor(sample_table, col_name="data")
    # PercentageDescriptor with chunk_size=None returns one row with percentage column
    assert "percentage" in result_table.colnames
    assert len(result_table) == 1
    # All values [0, 1, 2, 3, 4] are non-zero except 0, so 80% are non-zero
    assert result_table["percentage"][0] == pytest.approx(80.0)


@pytest.mark.verifies_usecase("UC-140-1.2.1")
def test_metric_apply_criteria(setup_metric, sample_table):
    """Test that criteria are correctly applied to the descriptor output."""
    metric = setup_metric
    result_table = metric(sample_table)

    # Check that result is a Table
    assert isinstance(result_table, Table)

    # Check that criterion pass column exists
    assert "RangeCriterion_pass" in result_table.colnames

    # Check that all time windows passed the criterion
    assert all(result_table["RangeCriterion_pass"])

    # Check overall_pass and pass_rate columns
    assert "overall_pass" in result_table.colnames
    assert "pass_rate" in result_table.colnames
    assert all(result_table["overall_pass"])
    np.testing.assert_allclose(result_table["pass_rate"], 1.0, rtol=1e-9, atol=1e-9)

    # Check metadata
    assert "metric_config" in result_table.meta
    assert "n_time_windows" in result_table.meta
    assert result_table.meta["n_time_windows"] == len(result_table)


@pytest.fixture()
def setup_telescope_specific_metric(test_subarray):
    """Setup a telescope-specific metric with test_subarray from conftest."""
    metric_config_yaml = """
    Metric:
      name: "test_telescope_metric"
      input_source: "dummy_data_file.h5:"
      table_column: "data"
      array_element: LST
      telescope_specific: True
      descriptor: PercentageDescriptor
      descriptor_column: percentage
      criteria:
        - TelescopeRangeCriterion:
            min_value:
              - [type, LST*, 0]
              - [type, MST*, 5]
            max_value:
              - [type, LST*, 100]
              - [type, MST*, 100]
    """
    metric_config_dict = yaml.safe_load(metric_config_yaml)
    metric_config = Config(metric_config_dict)
    metric = Metric(config=metric_config, subarray=test_subarray)
    return metric


@pytest.mark.verifies_usecase("UC-140-1.2.1")
def test_telescope_specific_metric_descriptor(
    setup_telescope_specific_metric, sample_table
):
    """Test that descriptor works for telescope-specific metrics (note: descriptor itself is not telescope-specific)."""
    metric = setup_telescope_specific_metric
    result_table = metric._descriptor(sample_table, col_name="data")
    # PercentageDescriptor returns percentage of non-zero values
    assert "percentage" in result_table.colnames
    # [0, 1, 2, 3, 4] has 80% non-zero values
    assert result_table["percentage"][0] == pytest.approx(80.0)


@pytest.mark.verifies_usecase("UC-140-1.2.1")
def test_telescope_specific_metric_apply_criteria(
    setup_telescope_specific_metric, sample_table
):
    """Test that telescope-specific criteria are correctly applied."""
    metric = setup_telescope_specific_metric
    result_table = metric(sample_table, tel_id=1)

    # Check that result is a Table
    assert isinstance(result_table, Table)

    # Check that criterion pass column exists
    assert "TelescopeRangeCriterion_pass" in result_table.colnames

    # Check that all time windows passed the criterion
    assert all(result_table["TelescopeRangeCriterion_pass"])

    # Check overall_pass and pass_rate
    assert all(result_table["overall_pass"])
    assert np.allclose(result_table["pass_rate"], 1.0)

    # Check metadata
    assert "metric_config" in result_table.meta
    assert result_table.meta["n_time_windows"] == len(result_table)


@pytest.fixture()
def setup_metric_with_multiple_criteria():
    metric_config_yaml = """
    Metric:
      name: "test_metric_multiple_criteria"
      input_source: "dummy_data_file.h5:"
      table_column: "data"
      array_element: DUMMY
      telescope_specific: False
      descriptor: PercentageDescriptor
      descriptor_column: percentage
      criteria:
        - RangeCriterion:
            min_value: 0
            max_value: 100
        - ThresholdCriterion:
            threshold: 85
            above: False
    """
    metric_config_dict = yaml.safe_load(metric_config_yaml)
    metric_config = Config(metric_config_dict)
    metric = Metric(config=metric_config)
    return metric


@pytest.mark.verifies_usecase("UC-140-1.2.1")
def test_metric_with_multiple_criteria_descriptor(
    setup_metric_with_multiple_criteria, sample_table
):
    """Test that descriptor works with multiple criteria."""
    metric = setup_metric_with_multiple_criteria
    result_table = metric._descriptor(sample_table, col_name="data")
    assert "percentage" in result_table.colnames
    # [0, 1, 2, 3, 4] has 80% non-zero
    assert result_table["percentage"][0] == pytest.approx(80.0)


@pytest.mark.verifies_usecase("UC-140-1.2.1")
def test_metric_with_multiple_criteria_apply_criteria(
    setup_metric_with_multiple_criteria, sample_table
):
    """Test that multiple criteria are correctly applied."""
    metric = setup_metric_with_multiple_criteria
    result_table = metric(sample_table)

    # Check that result is a Table
    assert isinstance(result_table, Table)

    # Check that both criterion pass columns exist
    assert "RangeCriterion_pass" in result_table.colnames
    assert "ThresholdCriterion_pass" in result_table.colnames

    # RangeCriterion: percentage (80%) is in range [0,100]
    assert all(result_table["RangeCriterion_pass"])

    # ThresholdCriterion: percentage (80%) should be < 85 (threshold=85, above=False)
    assert all(result_table["ThresholdCriterion_pass"])

    # overall_pass should be True since both criteria passed
    assert result_table["overall_pass"][0]

    # pass_rate should be 1.0 (2 of 2 criteria passed)
    assert np.isclose(result_table["pass_rate"][0], 1.0)

    # Check metadata
    assert "metric_config" in result_table.meta
    assert result_table.meta["n_time_windows"] == len(result_table)
