from distutils.version import LooseVersion
from textwrap import dedent
from io import StringIO

import numpy
import scipy
from scipy import stats
import pandas

from unittest import mock
import pytest
import pandas.testing as pdtest
from wqio.tests import helpers

from wqio.features import Location, Dataset
from wqio.datacollections import DataCollection, _dist_compare


OLD_SCIPY = LooseVersion(scipy.version.version) < LooseVersion("0.19")


def check_stat(expected_csv, result, comp=False):
    index_col = [0]
    if comp:
        index_col += [1]

    file_obj = StringIO(dedent(expected_csv))
    expected = pandas.read_csv(file_obj, header=[0, 1], index_col=index_col)

    if comp:
        expected = expected.stack(level=-1)

    pdtest.assert_frame_equal(
        expected.sort_index(axis="columns"),
        result.sort_index(axis="columns").round(6),
        atol=1e-5,
    )


def remove_g_and_h(group):
    return group.name[1] not in ["G", "H"]


@pytest.fixture
def dc():
    df = helpers.make_dc_data_complex()
    dc = DataCollection(
        df,
        rescol="res",
        qualcol="qual",
        stationcol="loc",
        paramcol="param",
        ndval="<",
        othergroups=None,
        pairgroups=["state", "bmp"],
        useros=True,
        filterfxn=remove_g_and_h,
        bsiter=10000,
    )

    return dc


@pytest.fixture
def dc_noNDs():
    df = helpers.make_dc_data_complex()
    dc = DataCollection(
        df,
        rescol="res",
        qualcol="qual",
        stationcol="loc",
        paramcol="param",
        ndval="junk",
        othergroups=None,
        pairgroups=["state", "bmp"],
        useros=True,
        filterfxn=remove_g_and_h,
        bsiter=10000,
    )

    return dc


def test_basic_attr(dc):
    assert dc._raw_rescol == "res"
    assert isinstance(dc.data, pandas.DataFrame)
    assert dc.roscol == "ros_res"
    assert dc.rescol == "ros_res"
    assert dc.qualcol == "qual"
    assert dc.stationcol == "loc"
    assert dc.paramcol == "param"
    assert dc.ndval == ["<"]
    assert dc.bsiter == 10000
    assert dc.groupcols == ["loc", "param"]
    assert dc.tidy_columns == ["loc", "param", "res", "__censorship"]
    assert hasattr(dc, "filterfxn")


def test_data(dc):
    assert isinstance(dc.data, pandas.DataFrame)
    assert dc.data.shape == (519, 8)
    assert "G" in dc.data["param"].unique()
    assert "H" in dc.data["param"].unique()


@pytest.mark.parametrize("useros", [True, False])
def test_tidy(dc, useros):
    assert isinstance(dc.tidy, pandas.DataFrame)
    assert dc.tidy.shape == (388, 5)
    assert "G" not in dc.tidy["param"].unique()
    assert "H" not in dc.tidy["param"].unique()
    collist = ["loc", "param", "res", "__censorship", "ros_res"]
    assert dc.tidy.columns.tolist() == collist


def test_paired(dc):
    assert isinstance(dc.paired, pandas.DataFrame)
    assert dc.paired.shape == (164, 6)
    assert "G" not in dc.paired.index.get_level_values("param").unique()
    assert "H" not in dc.paired.index.get_level_values("param").unique()
    dc.paired.columns.tolist() == [
        ("res", "Inflow"),
        ("res", "Outflow"),
        ("res", "Reference"),
        ("__censorship", "Inflow"),
        ("__censorship", "Outflow"),
        ("__censorship", "Reference"),
    ]


def test_count(dc):
    known_csv = """\
        station,Inflow,Outflow,Reference
        result,Count,Count,Count
        param,,,
        A,21,22,20
        B,24,22,19
        C,24,24,25
        D,24,25,21
        E,19,16,20
        F,21,24,17
    """
    check_stat(known_csv, dc.count)


def test_n_unique(dc):
    known_csv = """\
        loc,Inflow,Outflow,Reference
        result,bmp,bmp,bmp
        param,,,
        A,7,7,7
        B,7,7,7
        C,7,7,7
        D,7,7,7
        E,7,7,7
        F,7,7,7
        G,7,7,7
        H,7,7,7
    """
    check_stat(known_csv, dc.n_unique("bmp"))


@helpers.seed
def test_median(dc):
    known_csv = """\
        station,Inflow,Inflow,Inflow,Outflow,Outflow,Outflow,Reference,Reference,Reference
        result,lower,median,upper,lower,median,upper,lower,median,upper
        param,,,,,,,,,
        A,0.334506,1.197251,2.013994,0.860493,2.231058,2.626023,1.073386,1.639472,1.717293
        B,1.366948,2.773989,3.297147,0.23201,1.546499,2.579206,0.204164,1.565076,2.196367
        C,0.17351,0.525957,0.68024,0.247769,0.396984,0.540742,0.136462,0.412693,0.559458
        D,0.374122,1.201892,2.098846,0.516989,1.362759,1.827087,0.314655,0.882695,1.24545
        E,0.276095,1.070858,1.152887,0.287914,0.516746,1.456859,0.366824,0.80716,2.040739
        F,0.05667,0.832488,1.310575,0.425237,1.510942,2.193997,0.162327,0.745993,1.992513
    """
    check_stat(known_csv, dc.median)


@helpers.seed
def test_mean(dc):
    known_csv = """\
        station,Inflow,Inflow,Inflow,Outflow,Outflow,Outflow,Reference,Reference,Reference
        result,lower,mean,upper,lower,mean,upper,lower,mean,upper
        param,,,,,,,,,
        A,1.231607,2.646682,4.204054,1.930601,5.249281,9.081952,1.540167,3.777974,6.389439
        B,2.99031,7.647175,12.810844,1.545539,6.863835,12.705913,1.010374,4.504255,9.592572
        C,0.37496,0.513248,0.65948,0.411501,1.004637,1.706317,0.35779,0.541962,0.734751
        D,1.29141,3.021235,4.987855,1.285899,2.318808,3.451824,1.008364,1.945828,2.924812
        E,0.818641,1.914696,3.049554,0.584826,1.098241,1.640807,1.113589,2.283292,3.581946
        F,0.8379,9.825404,25.289933,1.497825,3.450184,5.61929,0.939917,2.491708,4.094258
    """
    check_stat(known_csv, dc.mean)


@helpers.seed
def test_std_dev(dc):
    known_csv = """\
        station,Inflow,Outflow,Reference
        result,std. dev.,std. dev.,std. dev.
        param,,,
        A,3.58649,8.719371,5.527633
        B,12.360099,13.60243,10.759285
        C,0.353755,1.691208,0.493325
        D,4.811938,2.849393,2.248178
        E,2.55038,1.096698,2.789238
        F,34.447565,5.361033,3.398367
    """
    check_stat(known_csv, dc.std_dev)


@helpers.seed
def test_percentile_25(dc):
    known_csv = """\
        station,Inflow,Outflow,Reference
        result,pctl 25,pctl 25,pctl 25
        param,,,
        A,0.522601,0.906029,1.094721
        B,1.472541,0.251126,0.314226
        C,0.164015,0.267521,0.136462
        D,0.35688,0.516989,0.383895
        E,0.364748,0.311508,0.394658
        F,0.120068,0.406132,0.224429
    """
    check_stat(known_csv, dc.percentile(25))


@helpers.seed
def test_percentile_75(dc):
    known_csv = """\
        station,Inflow,Outflow,Reference
        result,pctl 75,pctl 75,pctl 75
        param,,,
        A,2.563541,3.838021,2.650648
        B,4.728871,2.849948,2.261847
        C,0.776388,0.853535,0.792612
        D,3.04268,2.79341,3.611793
        E,1.532775,1.59183,3.201534
        F,1.792985,2.80979,2.742249
    """
    check_stat(known_csv, dc.percentile(75))


@helpers.seed
def test_logmean(dc):
    known_csv = """\
        station,Inflow,Inflow,Inflow,Outflow,Outflow,Outflow,Reference,Reference,Reference
        result,Log-mean,lower,upper,Log-mean,lower,upper,Log-mean,lower,upper
        param,,,,,,,,,
        A,0.140559,-0.55112,0.644202,0.733004,0.047053,1.22099,0.545205,-0.057683,1.029948
        B,1.026473,0.368659,1.541241,0.105106,-0.939789,0.860244,0.068638,-0.932357,0.661203
        C,-0.963004,-1.304115,-0.638446,-0.83221,-1.464092,-0.414379,-1.088377,-1.556795,-0.720706
        D,0.062317,-0.663241,0.58349,0.185757,-0.325074,0.598432,-0.063507,-0.670456,0.434214
        E,-0.103655,-0.751075,0.385909,-0.456202,-1.08692,0.029967,-0.068135,-0.787007,0.51226
        F,-0.442721,-1.874677,0.344704,0.211658,-0.504166,0.734283,-0.253352,-1.175917,0.467231
    """
    check_stat(known_csv, dc.logmean)


@helpers.seed
def test_logstd_dev(dc):
    known_csv = """\
        station,Inflow,Outflow,Reference
        result,Log-std. dev.,Log-std. dev.,Log-std. dev.
        param,,,
        A,1.374026,1.343662,1.225352
        B,1.430381,2.07646,1.662001
        C,0.818504,1.263631,1.057177
        D,1.530871,1.187246,1.277927
        E,1.264403,1.121038,1.474431
        F,2.324063,1.516331,1.701596
    """
    check_stat(known_csv, dc.logstd_dev)


@helpers.seed
def test_geomean(dc):
    known_csv = """\
        station,Inflow,Inflow,Inflow,Outflow,Outflow,Outflow,Reference,Reference,Reference
        Geo-mean,Log-mean,lower,upper,Log-mean,lower,upper,Log-mean,lower,upper
        param,,,,,,,,,
        A,1.150917,0.576304,1.904467,2.081323,1.048178,3.390543,1.724962,0.943949,2.800919
        B,2.791205,1.445795,4.670381,1.110829,0.39071,2.363737,1.071049,0.393625,1.937121
        C,0.381744,0.271413,0.528113,0.435087,0.231288,0.66075,0.336763,0.210811,0.486409
        D,1.064299,0.515179,1.792283,1.204129,0.722474,1.819264,0.938467,0.511475,1.543749
        E,0.901536,0.471859,1.470951,0.633686,0.337254,1.03042,0.934134,0.455205,1.66906
        F,0.642286,0.153405,1.411572,1.235726,0.604009,2.083988,0.776195,0.308536,1.595571
    """
    check_stat(known_csv, dc.geomean)


@helpers.seed
def test_geostd_dev(dc):
    known_csv = """\
        station,Inflow,Outflow,Reference
        Geo-std. dev.,Log-std. dev.,Log-std. dev.,Log-std. dev.
        param,,,
        A,3.951225,3.833055,3.405365
        B,4.180294,7.976181,5.269843
        C,2.267105,3.538244,2.878234
        D,4.622199,3.278041,3.589191
        E,3.540977,3.068036,4.368548
        F,10.217099,4.55548,5.48269
    """
    check_stat(known_csv, dc.geostd_dev)


@helpers.seed
def test_shapiro(dc):
    known_csv = """\
        station,Inflow,Inflow,Outflow,Outflow,Reference,Reference
        result,pvalue,statistic,pvalue,statistic,pvalue,statistic
        param,,,,,,
        A,1.8e-05,0.685783,1e-06,0.576069,4e-06,0.61735
        B,1e-06,0.594411,0.0,0.530962,0.0,0.41471
        C,0.028774,0.905906,0.0,0.546626,0.00279,0.860373
        D,1e-06,0.622915,1.5e-05,0.722374,0.000202,0.76518
        E,1.7e-05,0.654137,0.004896,0.818813,0.000165,0.74917
        F,0.0,0.292916,2e-06,0.634671,0.000167,0.713968
    """
    check_stat(known_csv, dc.shapiro)


@helpers.seed
def test_shapiro_log(dc):
    known_csv = """\
        station,Inflow,Inflow,Outflow,Outflow,Reference,Reference
        result,statistic,pvalue,statistic,pvalue,statistic,pvalue
        param,,,,,,
        A,0.983521938,0.96662426,0.979861856,0.913820148,0.939460814,0.234214202
        B,0.957531095,0.390856266,0.97048676,0.722278714,0.967978418,0.735424638
        C,0.906479359,0.029602444,0.974698305,0.78197974,0.967106879,0.572929323
        D,0.989704251,0.995502174,0.990663111,0.997093379,0.964812279,0.617747009
        E,0.955088913,0.479993254,0.95211035,0.523841977,0.963425279,0.61430341
        F,0.97542423,0.847370088,0.982230783,0.933124721,0.966197193,0.749036908
    """
    check_stat(known_csv, dc.shapiro_log)


@helpers.seed
def test_lilliefors(dc):
    known_csv = """\
        station,Inflow,Inflow,Outflow,Outflow,Reference,Reference
        result,lilliefors,pvalue,lilliefors,pvalue,lilliefors,pvalue
        param,,,,,,
        A,0.308131,1.4e-05,0.340594,0.0,0.364453,0.0
        B,0.36764,0.0,0.420343,0.0,0.417165,0.0
        C,0.166799,0.082737,0.324733,0.0,0.161753,0.090455
        D,0.273012,6.7e-05,0.240311,0.000665,0.296919,3.7e-05
        E,0.341398,3e-06,0.239314,0.014862,0.233773,0.005474
        F,0.419545,0.0,0.331315,0.0,0.284249,0.000741
    """
    check_stat(known_csv, dc.lilliefors)


@helpers.seed
def test_lilliefors_log(dc):
    known_csv = """\
        station,Inflow,Inflow,Outflow,Outflow,Reference,Reference
        result,log-lilliefors,pvalue,log-lilliefors,pvalue,log-lilliefors,pvalue
        param,,,,,,
        A,0.08548109,0.95458004,0.15443943,0.19715747,0.20141389,0.03268737
        B,0.16162839,0.10505016,0.12447902,0.49697902,0.15934334,0.22969362
        C,0.16957278,0.07248915,0.12388174,0.44379732,0.11746642,0.48915671
        D,0.06885549,0.99,0.06067356,0.99,0.13401954,0.41967483
        E,0.13506577,0.47186822,0.14552341,0.47797919,0.09164876,0.92860794
        F,0.14420794,0.30694533,0.08463267,0.92741885,0.08586933,0.9800294
    """
    check_stat(known_csv, dc.lilliefors_log)


@helpers.seed
def test_anderson_darling(dc):
    with helpers.raises(NotImplementedError):
        _ = dc.anderson_darling


@helpers.seed
def test_anderson_darling_log(dc):
    with helpers.raises(NotImplementedError):
        _ = dc.anderson_darling_log


@helpers.seed
def test_mann_whitney(dc):
    known_csv = """\
        ,,mann_whitney,mann_whitney,mann_whitney,pvalue,pvalue,pvalue
        loc_2,,Inflow,Outflow,Reference,Inflow,Outflow,Reference
        param,loc_1,,,,,,
        A,Inflow,,180.0,179.0,,0.2198330905,0.4263216587
        A,Outflow,282.0,,248.0,0.2198330905,,0.488580368
        A,Reference,241.0,192.0,,0.4263216587,0.488580368,
        B,Inflow,,345.0,317.0,,0.0766949991,0.0304383994
        B,Outflow,183.0,,216.0,0.0766949991,,0.8650586835
        B,Reference,139.0,202.0,,0.0304383994,0.8650586835,
        C,Inflow,,282.0,323.0,,0.9097070273,0.6527104406
        C,Outflow,294.0,,323.0,0.9097070273,,0.6527104406
        C,Reference,277.0,277.0,,0.6527104406,0.6527104406,
        D,Inflow,,285.0,263.0,,0.7718162376,0.8111960975
        D,Outflow,315.0,,293.0,0.7718162376,,0.5082395211
        D,Reference,241.0,232.0,,0.8111960975,0.5082395211,
        E,Inflow,,164.0,188.0,,0.7033493939,0.9663820218
        E,Outflow,140.0,,132.0,0.7033493939,,0.3813114322
        E,Reference,192.0,188.0,,0.9663820218,0.3813114322,
        F,Inflow,,201.0,172.0,,0.2505911218,0.8601783903
        F,Outflow,303.0,,236.0,0.2505911218,,0.4045186043
        F,Reference,185.0,172.0,,0.8601783903,0.4045186043
    """
    check_stat(known_csv, dc.mann_whitney, comp=True)


@helpers.seed
def test_t_test(dc):
    known_csv = """\
        ,,pvalue,pvalue,pvalue,t_test,t_test,t_test
        loc_2,,Inflow,Outflow,Reference,Inflow,Outflow,Reference
        param,loc_1,,,,,,
        A,Inflow,,0.2178424157,0.4563196599,,-1.2604458127,-0.7539785777
        A,Outflow,0.2178424157,,0.5240147979,1.2604458127,,0.643450194
        A,Reference,0.4563196599,0.5240147979,,0.7539785777,-0.643450194,
        B,Inflow,,0.8430007638,0.3898358794,,0.1992705833,0.869235357
        B,Outflow,0.8430007638,,0.5491097882,-0.1992705833,,0.6043850808
        B,Reference,0.3898358794,0.5491097882,,-0.869235357,-0.6043850808,
        C,Inflow,,0.1847386316,0.8191392537,,-1.3639360123,-0.2300373632
        C,Outflow,0.1847386316,,0.2179907667,1.3639360123,,1.2615982727
        C,Reference,0.8191392537,0.2179907667,,0.2300373632,-1.2615982727,
        D,Inflow,,0.5484265023,0.344783812,,0.6056706932,0.9582600001
        D,Outflow,0.5484265023,,0.6299742693,-0.6056706932,,0.4851636024
        D,Reference,0.344783812,0.6299742693,,-0.9582600001,-0.4851636024,
        E,Inflow,,0.2304569921,0.6770414622,,1.2287029977,-0.4198288251
        E,Outflow,0.2304569921,,0.1023435465,-1.2287029977,,-1.6935358498
        E,Reference,0.6770414622,0.1023435465,,0.4198288251,1.6935358498,
        F,Inflow,,0.422008391,0.3549979666,,0.8190789273,0.9463539528
        F,Outflow,0.422008391,,0.4988994144,-0.8190789273,,0.6826435968
        F,Reference,0.3549979666,0.4988994144,,-0.9463539528,-0.6826435968
    """
    check_stat(known_csv, dc.t_test, comp=True)


@helpers.seed
def test_levene(dc):
    known_csv = """\
        ,,levene,levene,levene,pvalue,pvalue,pvalue
        loc_2,,Inflow,Outflow,Reference,Inflow,Outflow,Reference
        param,loc_1,,,,,,
        A,Inflow,,1.176282059,0.293152155,,0.284450688,0.591287419
        A,Outflow,1.176282059,,0.397705309,0.284450688,,0.531863542
        A,Reference,0.293152155,0.397705309,,0.591287419,0.531863542,
        B,Inflow,,0.003559637,0.402002411,,0.952694449,0.529578712
        B,Outflow,0.003559637,,0.408938588,0.952694449,,0.526247443
        B,Reference,0.402002411,0.408938588,,0.529578712,0.526247443,
        C,Inflow,,1.965613561,0.679535532,,0.167626459,0.413910674
        C,Outflow,1.965613561,,1.462364363,0.167626459,,0.232602352
        C,Reference,0.679535532,1.462364363,,0.413910674,0.232602352,
        D,Inflow,,0.643364813,0.983777911,,0.426532092,0.32681669
        D,Outflow,0.643364813,,0.116830634,0.426532092,,0.734124856
        D,Reference,0.983777911,0.116830634,,0.32681669,0.734124856,
        E,Inflow,,0.961616536,0.410491665,,0.333914902,0.525668596
        E,Outflow,0.961616536,,2.726351564,0.333914902,,0.107912818
        E,Reference,0.410491665,2.726351564,,0.525668596,0.107912818,
        F,Inflow,,0.841984453,0.734809611,,0.363948105,0.396999375
        F,Outflow,0.841984453,,0.25881357,0.363948105,,0.613802541
        F,Reference,0.734809611,0.25881357,,0.396999375,0.613802541,
    """
    check_stat(known_csv, dc.levene, comp=True)


@helpers.seed
def test_wilcoxon(dc):
    known_csv = """\
        ,,wilcoxon,wilcoxon,wilcoxon,pvalue,pvalue,pvalue
        loc_2,,Inflow,Outflow,Reference,Inflow,Outflow,Reference
        param,loc_1,,,,,,
        A,Inflow,,32.0,59.0,,0.03479,0.430679
        A,Outflow,32.0,,46.0,0.03479,,0.274445
        A,Reference,59.0,46.0,,0.430679,0.274445,
        B,Inflow,,38.0,22.0,,0.600179,0.182338
        B,Outflow,38.0,,31.0,0.600179,,0.858863
        B,Reference,22.0,31.0,,0.182338,0.858863,
        C,Inflow,,75.0,120.0,,0.167807,0.601046
        C,Outflow,75.0,,113.0,0.167807,,0.463381
        C,Reference,120.0,113.0,,0.601046,0.463381,
        D,Inflow,,44.0,31.0,,0.593618,0.530285
        D,Outflow,44.0,,45.0,0.593618,,0.972125
        D,Reference,31.0,45.0,,0.530285,0.972125,
        E,Inflow,,21.0,19.0,,0.910156,0.386271
        E,Outflow,21.0,,16.0,0.910156,,0.077148
        E,Reference,19.0,16.0,,0.386271,0.077148,
        F,Inflow,,62.0,22.0,,0.492459,0.952765
        F,Outflow,62.0,,28.0,0.492459,,0.656642
        F,Reference,22.0,28.0,,0.952765,0.656642,
    """
    with pytest.warns(UserWarning):
        check_stat(known_csv, dc.wilcoxon, comp=True)


@helpers.seed
def test_ranksums(dc):
    known_csv = """\
        ,,pvalue,pvalue,pvalue,rank_sums,rank_sums,rank_sums
        loc_2,,Inflow,Outflow,Reference,Inflow,Outflow,Reference
        param,loc_1,,,,,,
        A,Inflow,,0.2153009,0.4187782,,-1.2391203,-0.8085428
        A,Outflow,0.2153009,,0.4807102,1.2391203,,0.7051607
        A,Reference,0.4187782,0.4807102,,0.8085428,-0.7051607,
        B,Inflow,,0.0748817,0.029513,,1.781188,2.1765661
        B,Outflow,0.0748817,,0.8547898,-1.781188,,0.1830104
        B,Reference,0.029513,0.8547898,,-2.1765661,-0.1830104,
        C,Inflow,,0.9015386,0.6455162,,-0.1237179,0.46
        C,Outflow,0.9015386,,0.6455162,0.1237179,,0.46
        C,Reference,0.6455162,0.6455162,,-0.46,-0.46,
        D,Inflow,,0.7641772,0.8023873,,-0.3,0.2502587
        D,Outflow,0.7641772,,0.5011969,0.3,,0.6726078
        D,Reference,0.8023873,0.5011969,,-0.2502587,-0.6726078,
        E,Inflow,,0.6911022,0.9551863,,0.3973597,-0.0561951
        E,Outflow,0.6911022,,0.3727144,-0.3973597,,-0.8914004
        E,Reference,0.9551863,0.3727144,,0.0561951,0.8914004,
        F,Inflow,,0.2459307,0.8486619,,-1.1602902,-0.190826
        F,Outflow,0.2459307,,0.3971011,1.1602902,,0.8468098
        F,Reference,0.8486619,0.3971011,,0.190826,-0.8468098,
    """
    check_stat(known_csv, dc.ranksums, comp=True)


@helpers.seed
@pytest.mark.xfail(OLD_SCIPY, reason="Scipy < 0.19")
def test_kendall(dc):
    known_csv = """\
        ,,kendalltau,kendalltau,kendalltau,pvalue,pvalue,pvalue
        loc_2,,Inflow,Outflow,Reference,Inflow,Outflow,Reference
        param,loc_1,,,,,,
        A,Inflow,,-0.051661,-0.00738,,0.772893,0.967114
        A,Outflow,-0.051661,,-0.083333,0.772893,,0.690095
        A,Reference,-0.00738,-0.083333,,0.967114,0.690095,
        B,Inflow,,0.441351,0.298246,,0.015267,0.119265
        B,Outflow,0.441351,,0.559855,0.015267,,0.004202
        B,Reference,0.298246,0.559855,,0.119265,0.004202,
        C,Inflow,,0.280223,0.084006,,0.078682,0.578003
        C,Outflow,0.280223,,-0.1417,0.078682,,0.352394
        C,Reference,0.084006,-0.1417,,0.578003,0.352394,
        D,Inflow,,0.403469,0.095299,,0.020143,0.634826
        D,Outflow,0.403469,,0.318337,0.020143,,0.094723
        D,Reference,0.095299,0.318337,,0.634826,0.094723,
        E,Inflow,,0.114286,0.640703,,0.673337,0.004476
        E,Outflow,0.114286,,0.167944,0.673337,,0.449603
        E,Reference,0.640703,0.167944,,0.004476,0.449603,
        F,Inflow,,0.0,0.07231,,1.0,0.763851
        F,Outflow,0.0,,0.388889,1.0,,0.063
        F,Reference,0.07231,0.388889,,0.763851,0.063,
    """
    check_stat(known_csv, dc.kendall, comp=True)


@helpers.seed
def test_spearman(dc):
    known_csv = """\
        ,,pvalue,pvalue,pvalue,spearmanrho,spearmanrho,spearmanrho
        loc_2,,Inflow,Outflow,Reference,Inflow,Outflow,Reference
        param,loc_1,,,,,,
        A,Inflow,,0.7574884491,0.9627447553,,-0.0809319588,0.012262418
        A,Outflow,0.7574884491,,0.7617330788,-0.0809319588,,-0.0823529412
        A,Reference,0.9627447553,0.7617330788,,0.012262418,-0.0823529412,
        B,Inflow,,0.0110829791,0.0775159774,,0.5831305575,0.4537313433
        B,Outflow,0.0110829791,,0.0024069317,0.5831305575,,0.6850916941
        B,Reference,0.0775159774,0.0024069317,,0.4537313433,0.6850916941,
        C,Inflow,,0.1330504059,0.6063501968,,0.3387640122,0.1134228342
        C,Outflow,0.1330504059,,0.3431640379,0.3387640122,,-0.2070506455
        C,Reference,0.6063501968,0.3431640379,,0.1134228342,-0.2070506455,
        D,Inflow,,0.0195715066,0.4751861062,,0.4935814032,0.1858231711
        D,Outflow,0.0195715066,,0.1263974782,0.4935814032,,0.363209462
        D,Reference,0.4751861062,0.1263974782,,0.1858231711,0.363209462,
        E,Inflow,,0.9828818202,0.0013596162,,0.0084033613,0.8112988341
        E,Outflow,0.9828818202,,0.3413722947,0.0084033613,,0.3012263814
        E,Reference,0.0013596162,0.3413722947,,0.8112988341,0.3012263814,
        F,Inflow,,0.9645303744,0.6759971848,,-0.0106277141,0.1348767061
        F,Outflow,0.9645303744,,0.0560590794,-0.0106277141,,0.5028571429
        F,Reference,0.6759971848,0.0560590794,,0.1348767061,0.5028571429
    """
    check_stat(known_csv, dc.spearman, comp=True)


@helpers.seed
def test_theilslopes(dc):
    with helpers.raises(NotImplementedError):
        _ = dc.theilslopes


def test_inventory(dc):
    known_csv = StringIO(
        dedent(
            """\
    loc,param,Count,Non-Detect
    Inflow,A,21,3
    Inflow,B,24,6
    Inflow,C,24,0
    Inflow,D,24,11
    Inflow,E,19,4
    Inflow,F,21,8
    Outflow,A,22,1
    Outflow,B,22,9
    Outflow,C,24,4
    Outflow,D,25,12
    Outflow,E,16,2
    Outflow,F,24,8
    Reference,A,20,2
    Reference,B,19,6
    Reference,C,25,4
    Reference,D,21,12
    Reference,E,20,3
    Reference,F,17,7
    """
        )
    )
    expected = pandas.read_csv(known_csv, index_col=[0, 1]).astype(int)
    pdtest.assert_frame_equal(expected, dc.inventory.astype(int), check_names=False)


def test_inventory_noNDs(dc_noNDs):
    known_csv = StringIO(
        dedent(
            """\
    loc,param,Count,Non-Detect
    Inflow,A,21,0
    Inflow,B,24,0
    Inflow,C,24,0
    Inflow,D,24,0
    Inflow,E,19,0
    Inflow,F,21,0
    Outflow,A,22,0
    Outflow,B,22,0
    Outflow,C,24,0
    Outflow,D,25,0
    Outflow,E,16,0
    Outflow,F,24,0
    Reference,A,20,0
    Reference,B,19,0
    Reference,C,25,0
    Reference,D,21,0
    Reference,E,20,0
    Reference,F,17,0
    """
        )
    )
    expected = pandas.read_csv(known_csv, index_col=[0, 1]).astype(int)
    pdtest.assert_frame_equal(
        expected, dc_noNDs.inventory.astype(int), check_names=False,
    )


@helpers.seed
def test_stat_summary(dc):
    known_csv = StringIO(
        dedent(
            """\
    ros_res,loc,A,B,C,D,E,F
    Count,Inflow,21,24,24,24,19,21
    Count,Outflow,22,22,24,25,16,24
    Count,Reference,20,19,25,21,20,17
    Non-Detect,Inflow,3.0,6.0,0.0,11.0,4.0,8.0
    Non-Detect,Outflow,1.0,9.0,4.0,12.0,2.0,8.0
    Non-Detect,Reference,2.0,6.0,4.0,12.0,3.0,7.0
    mean,Inflow,2.64668,7.64717,0.51325,3.02124,1.9147,9.8254
    mean,Outflow,5.24928,6.86384,1.00464,2.31881,1.09824,3.45018
    mean,Reference,3.77797,4.50425,0.54196,1.94583,2.28329,2.49171
    std,Inflow,3.67506,12.62594,0.36136,4.91543,2.62027,35.29825
    std,Outflow,8.92456,13.92253,1.72758,2.90815,1.13267,5.47634
    std,Reference,5.67123,11.05411,0.5035,2.3037,2.8617,3.50296
    min,Inflow,0.0756,0.17404,0.10213,0.05365,0.08312,0.00803
    min,Outflow,0.11177,0.02106,0.03578,0.11678,0.07425,0.06377
    min,Reference,0.15575,0.04909,0.04046,0.08437,0.05237,0.03445
    10%,Inflow,0.1772,0.45233,0.13467,0.15495,0.1763,0.03548
    10%,Outflow,0.44852,0.08297,0.08222,0.26949,0.19903,0.18008
    10%,Reference,0.38448,0.13467,0.08241,0.19355,0.12777,0.09457
    25%,Inflow,0.5226,1.47254,0.16401,0.35688,0.36475,0.12007
    25%,Outflow,0.90603,0.25113,0.26752,0.51699,0.31151,0.40613
    25%,Reference,1.09472,0.31423,0.13646,0.3839,0.39466,0.22443
    50%,Inflow,1.19725,2.77399,0.52596,1.20189,1.07086,0.83249
    50%,Outflow,2.23106,1.5465,0.39698,1.36276,0.51675,1.51094
    50%,Reference,1.63947,1.56508,0.41269,0.8827,0.80716,0.74599
    75%,Inflow,2.56354,4.72887,0.77639,3.04268,1.53278,1.79299
    75%,Outflow,3.83802,2.84995,0.85354,2.79341,1.59183,2.80979
    75%,Reference,2.65065,2.26185,0.79261,3.61179,3.20153,2.74225
    90%,Inflow,6.02835,24.40655,0.99293,8.00691,6.28345,8.51706
    90%,Outflow,12.43052,23.90022,2.43829,5.66731,2.30348,10.32829
    90%,Reference,12.58278,6.67125,1.2205,4.78255,7.72012,8.57303
    max,Inflow,13.87664,45.97893,1.26657,21.75505,8.88365,163.01001
    max,Outflow,36.58941,47.49381,8.04948,12.39894,4.19118,23.29367
    max,Reference,21.22363,48.23615,1.94442,7.67751,8.75609,10.5095
    """
        )
    )

    expected = pandas.read_csv(known_csv, index_col=[0, 1]).T
    pdtest.assert_frame_equal(
        expected.round(5),
        dc.stat_summary().round(5),
        check_names=False,
        check_dtype=False,
        rtol=1e-4,
    )


def test_locations(dc):
    for loc in dc.locations:
        assert isinstance(loc, Location)
    assert len(dc.locations) == 18
    assert dc.locations[0].definition == {"loc": "Inflow", "param": "A"}
    assert dc.locations[1].definition == {"loc": "Inflow", "param": "B"}


def test_datasets(dc):
    _ds = []
    for d in dc.datasets("Inflow", "Outflow"):
        assert isinstance(d, Dataset)
        _ds.append(d)
    assert len(_ds) == 6
    assert _ds[0].definition == {"param": "A"}
    assert _ds[1].definition == {"param": "B"}


# this sufficiently tests dc._filter_collection
def test_selectLocations(dc):
    locs = dc.selectLocations(param="A", loc=["Inflow", "Outflow"])
    assert len(locs) == 2
    for n, (loc, loctype) in enumerate(zip(locs, ["Inflow", "Outflow"])):
        assert isinstance(loc, Location)
        assert loc.definition["param"] == "A"
        assert loc.definition["loc"] == loctype


def test_selectLocations_squeeze_False(dc):
    locs = dc.selectLocations(param="A", loc=["Inflow"], squeeze=False)
    assert len(locs) == 1
    for n, loc in enumerate(locs):
        assert isinstance(loc, Location)
        assert loc.definition["param"] == "A"
        assert loc.definition["loc"] == "Inflow"


def test_selectLocations_squeeze_True(dc):
    loc = dc.selectLocations(param="A", loc=["Inflow"], squeeze=True)
    assert isinstance(loc, Location)
    assert loc.definition["param"] == "A"
    assert loc.definition["loc"] == "Inflow"


def test_selectLocations_squeeze_True_None(dc):
    loc = dc.selectLocations(param="A", loc=["Junk"], squeeze=True)
    assert loc is None


# since the test_selectLocations* tests stress _filter_collection
# enough, we'll mock it out for datasets:
def test_selectDatasets(dc):
    with mock.patch.object(dc, "_filter_collection") as _fc:
        with mock.patch.object(dc, "datasets", return_value=["A", "B"]) as _ds:
            dc.selectDatasets("Inflow", "Reference", foo="A", bar="C")
            _ds.assert_called_once_with("Inflow", "Reference")
            _fc.assert_called_once_with(["A", "B"], foo="A", bar="C", squeeze=False)


@pytest.mark.parametrize("func", [stats.mannwhitneyu, stats.wilcoxon])
@pytest.mark.parametrize(
    ("x", "all_same"), [([5, 5, 5, 5, 5], True), ([5, 6, 7, 7, 8], False)]
)
def test_dist_compare_wrapper(x, all_same, func):
    y = [5, 5, 5, 5, 5]
    with mock.patch.object(stats, func.__name__) as _test:
        result = _dist_compare(x, y, _test)
        if all_same:
            assert numpy.isnan(result.stat)
            assert numpy.isnan(result.pvalue)
            assert _test.call_count == 0
        else:
            # assert result == (0, 0)
            _test.assert_called_once_with(x, y, alternative="two-sided")
