import numpy as np
import pytest

from pandas._libs import iNaT

from pandas.core.dtypes.dtypes import PeriodDtype

import pandas as pd
from pandas.core.arrays import PeriodArray
from pandas.tests.extension import base


@pytest.fixture
def dtype():
    return PeriodDtype(freq="D")


@pytest.fixture
def data(dtype):
    return PeriodArray(np.arange(1970, 2070), freq=dtype.freq)


@pytest.fixture
def data_for_twos(dtype):
    return PeriodArray(np.ones(100) * 2, freq=dtype.freq)


@pytest.fixture
def data_for_sorting(dtype):
    return PeriodArray([2018, 2019, 2017], freq=dtype.freq)


@pytest.fixture
def data_missing(dtype):
    return PeriodArray([iNaT, 2017], freq=dtype.freq)


@pytest.fixture
def data_missing_for_sorting(dtype):
    return PeriodArray([2018, iNaT, 2017], freq=dtype.freq)


@pytest.fixture
def data_for_grouping(dtype):
    B = 2018
    NA = iNaT
    A = 2017
    C = 2019
    return PeriodArray([B, B, NA, NA, A, A, B, C], freq=dtype.freq)


@pytest.fixture
def na_value():
    return pd.NaT


class BasePeriodTests:
    pass


class TestPeriodDtype(BasePeriodTests, base.BaseDtypeTests):
    pass


class TestConstructors(BasePeriodTests, base.BaseConstructorsTests):
    pass


class TestGetitem(BasePeriodTests, base.BaseGetitemTests):
    pass


class TestMethods(BasePeriodTests, base.BaseMethodsTests):
    def test_combine_add(self, data_repeated):
        # Period + Period is not defined.
        pass


class TestInterface(BasePeriodTests, base.BaseInterfaceTests):

    pass


class TestArithmeticOps(BasePeriodTests, base.BaseArithmeticOpsTests):
    implements = {"__sub__", "__rsub__"}

    def test_arith_frame_with_scalar(self, data, all_arithmetic_operators):
        # frame & scalar
        if all_arithmetic_operators in self.implements:
            df = pd.DataFrame({"A": data})
            self.check_opname(df, all_arithmetic_operators, data[0], exc=None)
        else:
            # ... but not the rest.
            super().test_arith_frame_with_scalar(data, all_arithmetic_operators)

    def test_arith_series_with_scalar(self, data, all_arithmetic_operators):
        # we implement substitution...
        if all_arithmetic_operators in self.implements:
            s = pd.Series(data)
            self.check_opname(s, all_arithmetic_operators, s.iloc[0], exc=None)
        else:
            # ... but not the rest.
            super().test_arith_series_with_scalar(data, all_arithmetic_operators)

    def test_arith_series_with_array(self, data, all_arithmetic_operators):
        if all_arithmetic_operators in self.implements:
            s = pd.Series(data)
            self.check_opname(s, all_arithmetic_operators, s.iloc[0], exc=None)
        else:
            # ... but not the rest.
            super().test_arith_series_with_scalar(data, all_arithmetic_operators)

    def _check_divmod_op(self, s, op, other, exc=NotImplementedError):
        super()._check_divmod_op(s, op, other, exc=TypeError)

    def test_add_series_with_extension_array(self, data):
        # we don't implement + for Period
        s = pd.Series(data)
        msg = (
            r"unsupported operand type\(s\) for \+: "
            r"\'PeriodArray\' and \'PeriodArray\'"
        )
        with pytest.raises(TypeError, match=msg):
            s + data

    def test_error(self):
        pass

    @pytest.mark.parametrize("box", [pd.Series, pd.DataFrame])
    def test_direct_arith_with_ndframe_returns_not_implemented(self, data, box):
        # Override to use __sub__ instead of __add__
        other = pd.Series(data)
        if box is pd.DataFrame:
            other = other.to_frame()

        result = data.__sub__(other)
        assert result is NotImplemented


class TestCasting(BasePeriodTests, base.BaseCastingTests):
    pass


class TestComparisonOps(BasePeriodTests, base.BaseComparisonOpsTests):
    def _compare_other(self, s, data, op_name, other):
        # the base test is not appropriate for us. We raise on comparison
        # with (some) integers, depending on the value.
        pass


class TestMissing(BasePeriodTests, base.BaseMissingTests):
    pass


class TestReshaping(BasePeriodTests, base.BaseReshapingTests):
    pass


class TestSetitem(BasePeriodTests, base.BaseSetitemTests):
    pass


class TestGroupby(BasePeriodTests, base.BaseGroupbyTests):
    pass


class TestPrinting(BasePeriodTests, base.BasePrintingTests):
    pass


class TestParsing(BasePeriodTests, base.BaseParsingTests):
    @pytest.mark.parametrize("engine", ["c", "python"])
    def test_EA_types(self, engine, data):
        super().test_EA_types(engine, data)
