from __future__ import annotations

import shutil
from pathlib import Path

import geopandas as gpd
import pandas as pd
import pytest
from shapely import Polygon

import pydaymet as daymet
from pydaymet.cli import cli
from pydaymet.exceptions import (
    InputRangeError,
    InputTypeError,
    InputValueError,
    MissingCRSError,
    MissingItemError,
)

GEOM = Polygon(
    [[-69.77, 45.07], [-69.31, 45.07], [-69.31, 45.45], [-69.77, 45.45], [-69.77, 45.07]]
)
COORDS = (-69.77, 45.07)
DATES = ("2000-01-01", "2000-12-31")


def test_invalid_variable():
    with pytest.raises(InputValueError) as ex:
        _ = daymet.get_bycoords(COORDS, DATES, variables="tt")
    assert "variables" in str(ex.value)


def test_invalid_pet_timescale():
    with pytest.raises(InputValueError) as ex:
        _ = daymet.get_bycoords(COORDS, DATES, pet="hargreaves_samani", time_scale="monthly")
    assert "pet" in str(ex.value)


def test_invalid_timescale():
    with pytest.raises(InputValueError) as ex:
        _ = daymet.get_bycoords(COORDS, DATES, time_scale="subdaily")
    assert "time_scale" in str(ex.value)


def test_invalid_region():
    with pytest.raises(InputValueError) as ex:
        _ = daymet.get_bycoords(COORDS, DATES, region="nn")
    assert "region" in str(ex.value)


def test_invalid_coords():
    with pytest.raises(InputRangeError) as ex:
        _ = daymet.get_bycoords((0, 0), DATES)
    assert "-136.8989" in str(ex.value)


def test_invalid_date():
    with pytest.raises(InputRangeError) as ex:
        _ = daymet.get_bycoords(COORDS, ("1950-01-01", "2010-01-01"))
    assert "1980" in str(ex.value)


def test_invalid_year():
    with pytest.raises(InputRangeError) as ex:
        _ = daymet.get_bycoords(COORDS, 1950)
    assert "1980" in str(ex.value)


def test_invalid_year_type():
    with pytest.raises(InputTypeError) as ex:
        _ = daymet.get_bycoords(COORDS, "1950")
    assert "or int" in str(ex.value)


def test_invalid_date_tuple():
    with pytest.raises(InputTypeError) as ex:
        _ = daymet.get_bycoords(COORDS, ("2010-01-01"))
    assert "(start, end)" in str(ex.value)


class TestCLIFails:
    """Test the command-line interface exceptions."""

    def test_cli_missing_col(self, runner):
        params = {
            "id": "coords_test",
            "lon": -100,
            "start": "2000-01-01",
            "end": "2000-01-12",
            "pet": "hargreaves_samani",
        }
        coord_csv = "coords_missing_co.csv"
        save_dir = "test_cli_missing_col"
        df = pd.DataFrame(params, index=[0])
        df.to_csv(coord_csv)
        ret = runner.invoke(
            cli,
            [
                "coords",
                coord_csv,
                "-s",
                save_dir,
            ],
        )
        Path(coord_csv).unlink()
        shutil.rmtree(save_dir, ignore_errors=True)
        assert ret.exit_code == 1
        assert isinstance(ret.exception, MissingItemError)
        assert "lat" in str(ret.exception)

    def test_wrong_geo_format(self, runner):
        params = {
            "id": "geo_test",
            "start": "2000-01-01",
            "end": "2000-05-31",
        }
        geo_feather = "geo_wrong_format.feather"
        save_dir = "test_wrong_geo_format"
        gdf = gpd.GeoDataFrame(params, geometry=[GEOM], index=[0], crs=4326)
        gdf.to_feather(geo_feather)
        ret = runner.invoke(
            cli,
            [
                "geometry",
                geo_feather,
                "-s",
                save_dir,
            ],
        )
        Path(geo_feather).unlink()
        shutil.rmtree(save_dir, ignore_errors=True)
        assert ret.exit_code == 1
        assert isinstance(ret.exception, InputTypeError)
        assert "gpkg" in str(ret.exception)

    def test_wrong_geo_crs(self, runner):
        params = {
            "id": "geo_test",
            "start": "2000-01-01",
            "end": "2000-05-31",
        }
        geo_gpkg = Path("wrong_geo_crs.gpkg")
        save_dir = "test_wrong_geo_crs"
        gdf = gpd.GeoDataFrame(params, geometry=[GEOM], index=[0], crs=None)
        gdf.to_file(geo_gpkg)
        ret = runner.invoke(
            cli,
            [
                "geometry",
                str(geo_gpkg),
                "-s",
                save_dir,
            ],
        )
        if geo_gpkg.is_dir():
            shutil.rmtree(geo_gpkg)
        else:
            geo_gpkg.unlink()
        shutil.rmtree(save_dir, ignore_errors=True)
        assert ret.exit_code == 1
        assert isinstance(ret.exception, MissingCRSError)
        assert "CRS" in str(ret.exception)

    def test_wrong_coords_format(self, runner):
        params = {
            "id": "coords_test",
            "lon": -69.77,
            "lat": 45.07,
            "start": "2000-01-01",
            "end": "2000-12-31",
        }
        coord_paquet = "wrong_coords_format.paquet"
        save_dir = "test_wrong_coords_format"
        df = pd.DataFrame(params, index=[0])
        df.to_parquet(coord_paquet)
        ret = runner.invoke(
            cli,
            [
                "coords",
                coord_paquet,
                "-s",
                save_dir,
            ],
        )
        Path(coord_paquet).unlink()
        shutil.rmtree(save_dir, ignore_errors=True)
        assert ret.exit_code == 1
        assert isinstance(ret.exception, InputTypeError)
        assert "csv" in str(ret.exception)
