import os

from qgis.gui import QgisInterface
from qgis.PyQt import QtWidgets

from ..compat import MESSAGE_LEVEL_SUCCESS
from ..lib.unit_conversion import short_unit_name
from ..model.analysis import Analysis
from ..model.metric import Metric
from ..model.metric_value import MetricValue, load_metric_values
from ..model.project import Project
from ..model.sample_frame import get_sample_frame_ids
from ..QRiS.settings import Settings
from .frm_export_base import get_unique_export_path, sanitize_file_base_name
from .frm_export_table import FrmTableExport
from .utilities import add_standard_form_buttons


class FrmExportMetrics(QtWidgets.QDialog):
    def __init__(self, parent, iface: QgisInterface, project: Project, analysis: Analysis = None, current_dce=None, current_sf=None):
        super().__init__(parent)

        self.iface = iface
        self.project = project
        self.analysis = analysis
        self.current_dce = current_dce
        self.current_sf = current_sf
        self.base_name = "Analysis Metrics"
        self.file_base_name = sanitize_file_base_name(self.base_name)
        self.last_generated_path = None

        if self.analysis is not None:
            self.analyses = {analysis: get_sample_frame_ids(self.project.project_file, self.analysis.sample_frame.id)}
        else:
            self.analyses = {analysis: get_sample_frame_ids(self.project.project_file, analysis.sample_frame.id) for analysis in self.project.analyses.values()}

        self.setWindowTitle("Export Metrics Table")
        self.setupUi()

        if self.current_dce is None:
            self.rdoAllDCE.setChecked(True)
            # hide the groupbox for DCEs
            self.grpDCE.setEnabled(False)
            self.grpDCE.setVisible(False)

        if self.current_sf is None:
            self.rdoAllSF.setChecked(True)
            # hide the groupbox for SFs
            self.grpSF.setEnabled(False)
            self.grpSF.setVisible(False)

    def browse_path(self):

        output_format = self.combo_format.currentText()
        output_ext = "xls" if output_format == "Excel" else output_format.lower()

        initial = self.txtOutpath.text()
        folder = os.path.dirname(initial) if initial else ""
        path = QtWidgets.QFileDialog.getSaveFileName(self, "Export Metrics Table", folder, f"{output_format} Files (*.{output_ext})")[0]
        self.txtOutpath.setText(path)

    def update_default_path(self):

        home = self.project.project_file
        if not home:
            return

        if os.path.isfile(home):
            home = os.path.dirname(home)

        export_dir = os.path.join(home, "exports", self.file_base_name)
        if not os.path.exists(export_dir):
            os.makedirs(export_dir, exist_ok=True)

        output_format = self.combo_format.currentText()
        output_ext = ".xls" if output_format == "Excel" else f".{output_format.lower()}"

        default_path = get_unique_export_path(export_dir, self.file_base_name, output_ext)
        self.txtOutpath.setText(os.path.normpath(default_path))
        self.last_generated_path = self.txtOutpath.text()

    def format_change(self):

        current = self.txtOutpath.text()
        if current == "":
            self.update_default_path()
            return

        if current == self.last_generated_path:
            self.update_default_path()
            return

        output_format = self.combo_format.currentText()
        if output_format == "Excel":
            output_format = "xls"
        path = current[: current.rfind(".") + 1] + output_format.lower()
        self.txtOutpath.setText(path)

    def accept(self) -> None:

        if not self.txtOutpath.text():
            QtWidgets.QMessageBox.warning(self, "Export Metrics Table", "Please specify an output file.")
            return

        # try:
        out_values = []

        data_capture_events = list(self.project.events.values()) if self.rdoAllDCE.isChecked() else [self.current_dce]
        # Intrinsic: current_dce is None but we want one intrinsic pass, not all events.
        is_intrinsic = self.analysis is not None and self.analysis.is_simple_intrinsic_mode()
        if is_intrinsic:
            data_capture_events = [None]
        for analysis, sample_frame_ids in self.analyses.items():
            sample_frame_features = list(sample_frame_ids.values()) if self.rdoAllSF.isChecked() else [self.current_sf]
            for sample_frame_feature in sample_frame_features:
                for data_capture_event in data_capture_events:
                    metric_values = load_metric_values(self.project.project_file, analysis, data_capture_event, sample_frame_feature.id, self.project.metrics)
                    values = {
                        "analysis_name": analysis.name,
                        "sample_frame_id": sample_frame_feature.id,
                        "data_capture_event_id": data_capture_event.id if data_capture_event else "Intrinsic",
                        "sample_frame_feature_name": sample_frame_feature.name,
                        "data_capture_event_name": data_capture_event.name if data_capture_event else "Intrinsic",
                    }
                    for analysis_metric in analysis.analysis_metrics.values():
                        metric: Metric = analysis_metric.metric

                        # --- Consistent display unit logic ---
                        display_unit = analysis.units.get(metric.unit_type, None)
                        if metric.normalized and display_unit not in [None, "ratio", "count"]:
                            display_unit = analysis.units["distance"]
                        display_unit_for_value = None if display_unit in ["count", "ratio"] else display_unit

                        # --- Header logic: always use metric.unit_type for numerator ---
                        # Get the actual unit string for numerator
                        unit_str_raw = analysis.units.get(metric.unit_type, None)
                        numerator = short_unit_name(unit_str_raw) if unit_str_raw not in [None, "count", "ratio", "percent"] else ("#" if unit_str_raw == "count" else "%" if unit_str_raw == "percent" else "")

                        # For normalized metrics, use the actual normalization unit string
                        if metric.normalized and unit_str_raw not in [None, "ratio"]:
                            normalization_unit_raw = analysis.units.get("distance", "m")
                            denominator = short_unit_name(normalization_unit_raw)
                            unit_str = f"{numerator}/{denominator}" if numerator else f"/{denominator}"
                        else:
                            unit_str = numerator

                        metric_name = f"{metric.name} ({unit_str})" if unit_str else metric.name

                        metric_value: MetricValue = metric_values.get(metric.id, MetricValue(metric, None, None, False, None, None, metric.default_unit_id, None))

                        # --- Use current_value_as_string for formatting and conversion ---
                        value = metric_value.manual_value if metric_value.is_manual == 1 else metric_value.current_value_as_string(display_unit_for_value)
                        value = value if value is not None else ""
                        values.update({metric_name: value})

                        if self.chkIncludeUncertainty.isChecked():
                            uncertainty = metric_value.uncertainty_as_string()
                            values.update({f"{metric_name} Uncertainty": uncertainty})
                    out_values.append(values)

        if len(out_values) < 1:
            QtWidgets.QMessageBox.information(self, "Export Metrics Table", "No metrics were available for export.")
            return

        exporter = FrmTableExport(
            self,
            data=out_values,
            base_name="analysis_metrics",
            project_path=self.project.project_file,
            export_type="analysis_metrics",
        )
        success, msg = exporter.export_table(self.txtOutpath.text())
        if not success:
            QtWidgets.QMessageBox.critical(self, "Export Metrics Table", f"Error exporting metrics table: {msg}")
            return

        note = " including uncertainty columns" if self.chkIncludeUncertainty.isChecked() else ""
        Settings().msg_bar("Export Metrics", f"Exported metrics{note} to {self.txtOutpath.text()}", MESSAGE_LEVEL_SUCCESS)

        out_file = self.txtOutpath.text()
        super().accept()
        exporter.show_export_success(out_file)
        return

    def setupUi(self):

        self.setMinimumSize(500, 300)

        # Top level layout must include parent. Widgets added to this layout do not need parent.
        self.vert = QtWidgets.QVBoxLayout(self)
        self.setLayout(self.vert)

        # Groupbox named "Data Capture Events" with radio buttons for "just the currently active DCE" or "All Data Capture Events"
        self.grpDCE = QtWidgets.QGroupBox("Data Capture Events")
        self.rdoActiveDCE = QtWidgets.QRadioButton("Just the currently active Data Capture Event")
        self.rdoActiveDCE.setToolTip("Export metrics for the only currently active Data Capture Event")
        self.rdoAllDCE = QtWidgets.QRadioButton("All Data Capture Events")
        self.rdoAllDCE.setToolTip("Export metrics for all Data Capture Events in the project")
        self.rdoActiveDCE.setChecked(True)
        self.grpDCE.setLayout(QtWidgets.QVBoxLayout())
        self.grpDCE.layout().addWidget(self.rdoActiveDCE)
        self.grpDCE.layout().addWidget(self.rdoAllDCE)
        self.vert.addWidget(self.grpDCE)

        # groupbox named Sampling Frames with radio buttons for "just the currently active SF" or "All Sampling Frames"
        self.grpSF = QtWidgets.QGroupBox("Sample Frames")
        self.rdoActiveSF = QtWidgets.QRadioButton("Just the currently active Sample Frame")
        self.rdoActiveSF.setToolTip("Export metrics for the only currently active Sample Frame")
        self.rdoAllSF = QtWidgets.QRadioButton("All Sample Frames")
        self.rdoAllSF.setToolTip("Export metrics for all Sample Frames in the project")
        self.rdoActiveSF.setChecked(True)
        self.grpSF.setLayout(QtWidgets.QVBoxLayout())
        self.grpSF.layout().addWidget(self.rdoActiveSF)
        self.grpSF.layout().addWidget(self.rdoAllSF)
        self.vert.addWidget(self.grpSF)

        self.export_grid = QtWidgets.QGridLayout()
        self.vert.addLayout(self.export_grid)

        lbl_info = QtWidgets.QLabel("Exporting:")
        self.export_grid.addWidget(lbl_info, 0, 0, 1, 1)
        lbl_name = QtWidgets.QLabel(f"<b>{self.base_name}</b>")
        self.export_grid.addWidget(lbl_name, 0, 1, 1, 1)

        # check box for including uncertainty
        self.chkIncludeUncertainty = QtWidgets.QCheckBox("Include Value Uncertainty Columns in Export")
        self.chkIncludeUncertainty.setToolTip("Include the uncertainty value for each metric in the export")
        self.chkIncludeUncertainty.setChecked(False)
        self.export_grid.addWidget(self.chkIncludeUncertainty, 1, 0, 1, 2)

        # label for export format
        self.lbl_format = QtWidgets.QLabel("Output Format:")
        self.export_grid.addWidget(self.lbl_format, 2, 0, 1, 1)

        self.horiz_format = QtWidgets.QHBoxLayout()
        self.export_grid.addLayout(self.horiz_format, 2, 1, 1, 1)

        # drop down for export format
        self.combo_format = QtWidgets.QComboBox()
        self.combo_format.setToolTip("Select the format to export the metrics table")
        self.combo_format.addItems(["CSV", "JSON", "Excel"])
        self.combo_format.currentTextChanged.connect(self.format_change)
        self.horiz_format.addWidget(self.combo_format)

        # add spacer
        self.horiz_format.addStretch()

        # label for export location
        self.lbl_location = QtWidgets.QLabel("Output Path:")
        self.export_grid.addWidget(self.lbl_location, 3, 0, 1, 1)

        self.horizOutput = QtWidgets.QHBoxLayout()
        self.export_grid.addLayout(self.horizOutput, 3, 1, 1, 1)

        # line edit for export location
        self.txtOutpath = QtWidgets.QLineEdit()
        self.txtOutpath.setToolTip("Specify the location to save the exported metrics table")
        self.txtOutpath.setReadOnly(True)
        self.horizOutput.addWidget(self.txtOutpath)

        # button for export location
        self.btn_location = QtWidgets.QPushButton("...")
        self.btn_location.setFixedWidth(30)
        self.btn_location.setToolTip("Browse to the directory to save the exported metrics table")
        self.btn_location.clicked.connect(self.browse_path)
        self.horizOutput.addWidget(self.btn_location)

        self.update_default_path()

        # add vertical spacer
        self.vert.addStretch()

        # add standard form buttons
        self.vert.addLayout(add_standard_form_buttons(self, "analyses#export-an-analysis"))
