"""
/***************************************************************************
 LDMP - A QGIS plugin
 This plugin supports monitoring and reporting of land degradation to the UNCCD
 and in support of the SDG Land Degradation Neutrality (LDN) target.
                              -------------------
        begin                : 2017-05-23
        git sha              : $Format:%H$
        copyright            : (C) 2017 by Conservation International
        email                : trends.earth@conservation.org
 ***************************************************************************/
"""

# pylint: disable=import-error
import typing
from pathlib import Path

import qgis.gui
from qgis.core import QgsGeometry
from qgis.PyQt import QtCore, QtWidgets, uic
from qgis.PyQt.QtWidgets import QButtonGroup
from te_schemas.algorithms import ExecutionScript

from . import conf
from .calculate import DlgCalculateBase
from .jobs.manager import job_manager
from .localexecution import drought
from .logger import log

DlgCalculateDroughtUi, _ = uic.loadUiType(
    str(Path(__file__).parent / "gui/DlgCalculateDrought.ui")
)
DlgCalculateDroughtSummaryUi, _ = uic.loadUiType(
    str(Path(__file__).parent / "gui/DlgCalculateDroughtSummary.ui")
)


class tr_calculate_drought:
    def tr(self, message):
        return QtCore.QCoreApplication.translate("tr_calculate_drought", message)


class DlgCalculateDrought(DlgCalculateBase, DlgCalculateDroughtUi):
    def __init__(
        self,
        iface: qgis.gui.QgisInterface,
        script: ExecutionScript,
        parent: QtWidgets.QWidget = None,
    ):
        super().__init__(iface, script, parent)
        self.setupUi(self)

        # TODO: add an option of different population and SPI datasets...
        self.population_dataset_name = "Gridded Population Count (gender breakdown)"
        self.population_dataset = conf.REMOTE_DATASETS["WorldPop"][
            self.population_dataset_name
        ]

        self.spi_dataset_name = "GPCC V6 (Global Precipitation Climatology Centre)"
        self.spi_dataset = conf.REMOTE_DATASETS["SPI"][self.spi_dataset_name]

        # Need a land cover dataset to mask out water areas
        self.lc_dataset_name = "ESA CCI"
        self.lc_dataset = conf.REMOTE_DATASETS["Land cover"][self.lc_dataset_name]

        self.precip_button_group = QButtonGroup(self)
        buttons = [
            self.precip_chirps_radio,
            self.precip_gpcc_radio,
            self.precip_ukceh_spei_radio,
            self.precip_ukceh_spi_radio,
        ]
        for i, btn in enumerate(buttons, start=1):
            self.precip_button_group.addButton(btn, i)

        self.precip_button_group.buttonClicked.connect(self.update_spi_dataset)

        # Connect the population constraint checkbox to update time bounds
        self.constrain_to_population_cb.stateChanged.connect(self.update_time_bounds)

        self.update_time_bounds()
        self.populate_spi_lags()
        self.lag_cb.setCurrentIndex(self.lag_cb.findText("12"))

        self._finish_initialization()

    def populate_spi_lags(self):
        self.lag_cb.addItems([str(lag) for lag in self.spi_dataset["Lags"]])

    def update_spi_dataset(self) -> None:
        """
        Update the SPI dataset based on the selected precipitation dataset.
        """
        if self.precip_chirps_radio.isChecked():
            self.spi_dataset_name = "CHIRPS v2025"
            self.spi_dataset = conf.REMOTE_DATASETS["SPI"][self.spi_dataset_name]
            # Add note to setup_frame about CHIRPS v2025 coverage if not already present
            if not self.setup_frame.findChild(QtWidgets.QLabel, "note_label"):
                note_label = QtWidgets.QLabel(
                    self.tr(
                        "Note: The CHIRPS v2025 dataset is limited to quasi-global coverage, spanning latitudes from 50°S to 50°N and all longitudes. As such, it does not include data for regions beyond ±50° latitude, including the polar areas."
                    )
                )
                note_label.setObjectName("note_label")
                note_label.setWordWrap(True)
                self.setup_frame.layout().addWidget(note_label)
        else:
            # Remove note_label if it exists
            note_label = self.setup_frame.findChild(QtWidgets.QLabel, "note_label")
            if note_label:
                self.setup_frame.layout().removeWidget(note_label)
                note_label.deleteLater()
            if self.precip_gpcc_radio.isChecked():
                self.spi_dataset_name = (
                    "GPCC V6 (Global Precipitation Climatology Centre)"
                )
                self.spi_dataset = conf.REMOTE_DATASETS["SPI"][self.spi_dataset_name]
            elif self.precip_ukceh_spi_radio.isChecked():
                self.spi_dataset_name = "UKCEH SPI"
                self.spi_dataset = conf.REMOTE_DATASETS["SPI"][self.spi_dataset_name]
            elif self.precip_ukceh_spei_radio.isChecked():
                self.spi_dataset_name = "UKCEH SPEI"
                self.spi_dataset = conf.REMOTE_DATASETS["SPI"][self.spi_dataset_name]

        # Update the lag combo box with the new SPI dataset lags
        self.lag_cb.clear()
        self.populate_spi_lags()

        # Update time bounds after dataset change
        self.update_time_bounds()

    def update_time_bounds(self):
        start_year_spi = self.spi_dataset["Start year"]
        end_year_spi = self.spi_dataset["End year"]

        if self.constrain_to_population_cb.isChecked():
            # Constrain to intersection of population and SPI data availability
            start_year_pop = self.population_dataset["Start year"]
            end_year_pop = self.population_dataset["End year"]
            start_year = QtCore.QDate(max(start_year_pop, start_year_spi), 1, 1)
            end_year = QtCore.QDate(min(end_year_pop, end_year_spi), 1, 1)
        else:
            # Use only SPI data availability (precipitation dataset)
            start_year = QtCore.QDate(start_year_spi, 1, 1)
            end_year = QtCore.QDate(end_year_spi, 1, 1)

        self.year_initial_de.setMinimumDate(start_year)
        self.year_initial_de.setMaximumDate(end_year)
        self.year_final_de.setMinimumDate(start_year)
        self.year_final_de.setMaximumDate(end_year)

    def btn_calculate(self):
        # Note that the super class has several tests in it - if they fail it
        # returns False, which would mean this function should stop execution
        # as well.
        ret = super().btn_calculate()

        if not ret:
            return

        crosses_180th, geojsons = self.gee_bounding_box

        year_initial = self.year_initial_de.date().year()
        year_final = self.year_final_de.date().year()

        if (year_final - year_initial) < 5:
            QtWidgets.QMessageBox.warning(
                None,
                self.tr("Error"),
                self.tr(
                    "Initial and final year are less 5 years "
                    "apart in - results will be more reliable "
                    "if more data (years) are included in the analysis."
                ),
            )

            return

        payload = {}
        payload["population"] = {
            "asset": self.population_dataset["GEE Dataset"],
            "source": self.population_dataset_name,
        }

        # Add fake_data parameter if population constraint is disabled
        if not self.constrain_to_population_cb.isChecked():
            payload["population"]["fake_data"] = True

        payload["land_cover"] = {
            "asset": self.lc_dataset["GEE Dataset"],
            "source": self.lc_dataset_name,
        }

        payload["spi"] = {
            "asset": self.spi_dataset["GEE Dataset"],
            "source": self.spi_dataset_name,
            "lag": int(self.lag_cb.currentText()),
        }

        payload.update(
            {
                "geojsons": geojsons,
                "crs": self.aoi.get_crs_dst_wkt(),
                "crosses_180th": crosses_180th,
                "task_name": self.execution_name_le.text(),
                "task_notes": self.task_notes.toPlainText(),
                "script": ExecutionScript.Schema().dump(self.script),
                "year_initial": year_initial,
                "year_final": year_final,
            }
        )

        self.close()

        resp = job_manager.submit_remote_job(payload, self.script.id)

        if resp:
            main_msg = "Submitted"
            description = "Drought indicator task submitted to Trends.Earth server."
        else:
            main_msg = "Error"
            description = (
                "Unable to submit drought indicator task to Trends.Earth server."
            )
        self.mb.pushMessage(
            self.tr(main_msg), self.tr(description), level=0, duration=5
        )

        self.close()


class DlgCalculateDroughtSummary(DlgCalculateBase, DlgCalculateDroughtSummaryUi):
    LOCAL_SCRIPT_NAME: str = "drought-vulnerability-summary"

    button_calculate: QtWidgets.QPushButton
    combo_boxes: typing.Dict[str, drought.SummaryTableDroughtWidgets] = {}

    def __init__(
        self,
        iface: qgis.gui.QgisInterface,
        script: ExecutionScript,
        parent: QtWidgets.QWidget = None,
    ):
        super().__init__(iface, script, parent)
        self.setupUi(self)

        self._finish_initialization()

        self.combo_boxes = drought.SummaryTableDroughtWidgets(
            combo_dataset_drought=self.combo_dataset_drought,
            combo_layer_so3_vulnerability=self.combo_layer_so3_vulnerability,
        )

        self.changed_region.connect(self.combo_boxes.populate)

    def showEvent(self, event):
        super().showEvent(event)
        self.combo_boxes.populate()

    def _validate_dataset_selection(self, combo_box, dataset_name):
        if len(combo_box.dataset_list) == 0:
            QtWidgets.QMessageBox.critical(
                None,
                self.tr("Error"),
                self.tr(
                    f"You must select a {dataset_name} layer "
                    "before you can use the drought tool."
                ),
            )
            return False
        else:
            return True

    def _validate_layer_selection(self, combo_box, layer_name):
        return not combo_box.show_validation_error(self, layer_name, "the drought tool")

    def validate_dataset_selections(self, combo_boxes):
        """validate all needed layers are selected"""
        if not self._validate_dataset_selection(
            combo_boxes.combo_dataset_drought, "drought hazard and exposure"
        ):
            return False
        elif not self._validate_layer_selection(
            combo_boxes.combo_layer_so3_vulnerability, "vulnerability"
        ):
            return False
        else:
            return True

    def validate_layer_extents(self, combo_boxes):
        """Check that the layers cover the full extent of the AOI"""

        if (
            self.aoi.calc_frac_overlap(
                QgsGeometry.fromRect(
                    combo_boxes.combo_dataset_drought.get_current_extent()
                )
            )
            < 0.99
        ):
            QtWidgets.QMessageBox.critical(
                None,
                self.tr("Error"),
                self.tr("Area of interest is not entirely within the drought dataset."),
            )

            return False

        return True

    def btn_calculate(self):
        # Note that the super class has several tests in it - if they fail it
        # returns False, which would mean this function should stop execution
        # as well.
        ret = super().btn_calculate()

        if not ret:
            log(f"failed tests in main class validation (return value {ret})")
            return

        elif not self.validate_dataset_selections(
            self.combo_boxes
        ) or not self.validate_layer_extents(self.combo_boxes):
            log("failed layer validation")

            return
        else:
            log("passed all validation checks")

        task_name = self.execution_name_le.text().strip()
        if not task_name:
            task_name = self.script.name_readable or self.script.name

        task_notes = self.task_notes.toPlainText()

        params = {
            "task_name": task_name,
            "task_notes": task_notes,
        }
        params.update(
            drought.get_main_drought_summary_job_params(
                task_name=task_name,
                aoi=self.aoi,
                combo_dataset_drought=self.combo_boxes.combo_dataset_drought,
                combo_layer_so3_vulnerability=self.combo_boxes.combo_layer_so3_vulnerability,
                task_notes=task_notes,
            )
        )
        job_manager.submit_local_job_as_qgstask(
            params, script_name=self.LOCAL_SCRIPT_NAME, area_of_interest=self.aoi
        )

        self.close()
