# -*- coding: utf-8 -*-
"""Setup tab: get a first-time user connected to Earth Engine.

Four checks, in the order they can fail, each with the one action that
fixes it:

  1. earthengine-api installed in the Python that runs QGIS
  2. Google credentials stored on this machine
  3. a Cloud project registered for Earth Engine
  4. a live round trip to Google's servers

Also carries the boundary-data setting, because "which boundaries do I
use" is the other thing a new user outside Cambodia has to decide.
"""

import os
import sys

from qgis.PyQt.QtCore import Qt
from qgis.PyQt.QtGui import QDesktopServices, QFont
from qgis.PyQt.QtCore import QUrl
from qgis.PyQt.QtWidgets import (
    QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QGroupBox,
    QLineEdit, QTextEdit, QMessageBox, QApplication, QScrollArea,
    QFileDialog, QComboBox, QFormLayout, QDialog,
)

from .. import gee_utils as gu

OK_COLOR = "#0a7a35"
BAD_COLOR = "#c0392b"
WARN_COLOR = "#b8860b"


def _dot(state):
    """Coloured status glyph."""
    return {"ok": "OK", "bad": "X", "unknown": "?"}.get(state, "?")


class _Step(QGroupBox):
    """One check: title, status line, detail box, and an action button."""

    def __init__(self, number, title, action_text, action_cb, parent=None):
        super().__init__("{}. {}".format(number, title), parent)
        v = QVBoxLayout(self)
        v.setSpacing(6)

        self.lbl_state = QLabel("?  not checked yet")
        f = QFont()
        f.setBold(True)
        self.lbl_state.setFont(f)
        v.addWidget(self.lbl_state)

        self.txt = QLabel("")
        self.txt.setWordWrap(True)
        self.txt.setTextInteractionFlags(
            Qt.TextInteractionFlag.TextSelectableByMouse)
        self.txt.setStyleSheet("color:#555;")
        v.addWidget(self.txt)

        h = QHBoxLayout()
        self.btn = QPushButton(action_text)
        self.btn.clicked.connect(action_cb)
        h.addWidget(self.btn)
        h.addStretch(1)
        self.extra = h
        v.addLayout(h)

    def set_state(self, state, message):
        color = {"ok": OK_COLOR, "bad": BAD_COLOR,
                 "unknown": WARN_COLOR}.get(state, WARN_COLOR)
        label = {"ok": "ready", "bad": "needs attention",
                 "unknown": "not checked yet"}.get(state, "")
        self.lbl_state.setText("{}  {}".format(_dot(state), label))
        self.lbl_state.setStyleSheet("color:{};".format(color))
        self.txt.setText(message or "")


class SetupDialog(QWidget):
    """Embedded as the Setup tab; also usable as a standalone panel."""

    def __init__(self, iface, parent=None):
        super().__init__(parent)
        self.iface = iface
        self._build()
        self.refresh()

    # ---------------------------------------------------------------- UI
    def _build(self):
        root = QVBoxLayout(self)
        scroll = QScrollArea()
        scroll.setWidgetResizable(True)
        scroll.setFrameShape(QScrollArea.Shape.NoFrame)
        content = QWidget()
        v = QVBoxLayout(content)

        intro = QLabel(
            "<b>First time here?</b> Work down these four steps. Each one "
            "checks a single thing and tells you exactly what to do if it "
            "is not ready. You only need to do this once per computer.")
        intro.setWordWrap(True)
        intro.setStyleSheet(
            "background:#eef4ff; border:1px solid #c3d5f0; padding:8px;")
        v.addWidget(intro)

        self.s1 = _Step(1, "Earth Engine Python package",
                        "Show install command", self._fix_package)
        v.addWidget(self.s1)

        self.s2 = _Step(2, "Sign in to Google",
                        "Authenticate ...", self._authenticate)
        v.addWidget(self.s2)

        self.s3 = _Step(3, "Cloud project registered for Earth Engine",
                        "Open Google Cloud console", self._open_console)
        row = QHBoxLayout()
        row.addWidget(QLabel("Project ID:"))
        self.ed_project = QLineEdit(gu.ee_project_id())
        self.ed_project.setPlaceholderText("e.g. my-ee-project-12345")
        self.ed_project.editingFinished.connect(self._save_project)
        row.addWidget(self.ed_project, 1)
        btn_save = QPushButton("Save")
        btn_save.clicked.connect(self._save_project)
        row.addWidget(btn_save)
        self.s3.layout().addLayout(row)
        hint = QLabel(
            "Use the project <i>ID</i>, not the display name. In the Cloud "
            "console the ID appears under the project name and usually has "
            "a number on the end.")
        hint.setWordWrap(True)
        hint.setStyleSheet("color:#777; font-size:11px;")
        self.s3.layout().addWidget(hint)
        v.addWidget(self.s3)

        self.s4 = _Step(4, "Test the connection",
                        "Run test", self._test)
        v.addWidget(self.s4)

        # ---- boundary data source
        gb = QGroupBox("Boundary data for the map layout (Tool 6)")
        fb = QVBoxLayout(gb)
        note = QLabel(
            "The layout style - insets, legend, scale bar, grid - is not "
            "Cambodia-specific. Point this at your own admin polygons and "
            "the same cascade and map design apply anywhere.")
        note.setWordWrap(True)
        note.setStyleSheet("color:#555;")
        fb.addWidget(note)

        f = QFormLayout()
        self.cb_src = QComboBox()
        self.cb_src.addItems(["Cambodia (bundled NCDD boundaries)",
                              "Custom folder (any country)"])
        self.cb_src.currentIndexChanged.connect(self._src_changed)
        f.addRow("Source:", self.cb_src)

        hrow = QHBoxLayout()
        self.ed_folder = QLineEdit()
        self.ed_folder.setPlaceholderText(
            "folder holding your admin polygon files")
        btn_browse = QPushButton("Browse ...")
        btn_browse.clicked.connect(self._browse_folder)
        hrow.addWidget(self.ed_folder, 1)
        hrow.addWidget(btn_browse)
        w_row = QWidget()
        w_row.setLayout(hrow)
        f.addRow("Folder:", w_row)
        fb.addLayout(f)

        h2 = QHBoxLayout()
        btn_apply = QPushButton("Apply boundary source")
        btn_apply.clicked.connect(self._apply_source)
        h2.addWidget(btn_apply)
        h2.addStretch(1)
        fb.addLayout(h2)

        self.txt_src = QTextEdit()
        self.txt_src.setReadOnly(True)
        self.txt_src.setMaximumHeight(150)
        self.txt_src.setStyleSheet("font-family: monospace; font-size:11px;")
        fb.addWidget(self.txt_src)

        tip = QLabel(
            "<b>What to put in the folder:</b> one polygon file per level, "
            "coarse to fine (country, province/state, district, commune). "
            "GADM, FAO GAUL and OCHA COD downloads work as-is - the name, "
            "code and parent-code fields are detected automatically. "
            "Levels are matched from the file names (admin1, adm2, "
            "province, district ...) or, failing that, from how many "
            "polygons each file has.")
        tip.setWordWrap(True)
        tip.setStyleSheet("color:#777; font-size:11px;")
        fb.addWidget(tip)
        v.addWidget(gb)

        v.addStretch(1)
        scroll.setWidget(content)
        root.addWidget(scroll)

        h = QHBoxLayout()
        self.btn_refresh = QPushButton("Re-check everything")
        self.btn_refresh.clicked.connect(self.refresh)
        h.addWidget(self.btn_refresh)
        h.addStretch(1)
        self.lbl_overall = QLabel("")
        self.lbl_overall.setWordWrap(True)
        h.addWidget(self.lbl_overall, 1)
        root.addLayout(h)

        self._load_source_settings()

    # ------------------------------------------------------------- checks
    def refresh(self):
        ok, msg = gu.ee_package_status()
        self.s1.set_state("ok" if ok else "bad", msg)
        self.s2.btn.setEnabled(ok)

        ok2, msg2 = gu.ee_auth_status()
        self.s2.set_state("ok" if ok2 else "bad", msg2)

        ok3, msg3 = gu.ee_project_status()
        self.s3.set_state("ok" if ok3 else "bad", msg3)

        self.s4.set_state("unknown",
                          "Press 'Run test' to contact Earth Engine.")
        self.s4.btn.setEnabled(ok and ok2 and ok3)

        if ok and ok2 and ok3:
            self.lbl_overall.setText(
                "<span style='color:{}'>Steps 1-3 are ready. Run the test "
                "to confirm.</span>".format(OK_COLOR))
        else:
            missing = [n for n, good in
                       (("package", ok), ("sign-in", ok2),
                        ("project ID", ok3)) if not good]
            self.lbl_overall.setText(
                "<span style='color:{}'>Still needed: {}.</span>".format(
                    BAD_COLOR, ", ".join(missing)))

    # ------------------------------------------------------------ actions
    def _fix_package(self):
        exe = sys.executable or "python"
        cmd = '"{}" -m pip install --upgrade earthengine-api'.format(exe)
        box = QMessageBox(self)
        box.setWindowTitle("Install earthengine-api")
        box.setTextFormat(Qt.TextFormat.RichText)
        box.setText(
            "<b>Install the Earth Engine package</b><br><br>"
            "On Windows, open the <b>OSGeo4W Shell</b> from the Start menu "
            "(not the normal Command Prompt). On macOS or Linux, open a "
            "terminal. Then run:<br><br>"
            "<tt>{}</tt><br><br>"
            "Restart QGIS afterwards, then press 'Re-check everything'."
            .format(cmd))
        copy_btn = box.addButton("Copy command",
                                 QMessageBox.ButtonRole.ActionRole)
        box.addButton(QMessageBox.StandardButton.Close)
        box.exec()
        if box.clickedButton() is copy_btn:
            QApplication.clipboard().setText(cmd)

    def _authenticate(self):
        ans = QMessageBox.question(
            self, "Authenticate with Google",
            "A browser window will open so you can sign in with the Google "
            "account that is registered for Earth Engine.\n\n"
            "Copy the code it gives you back into the prompt if asked. "
            "QGIS may look frozen while this happens.\n\nContinue?",
            QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
        if ans != QMessageBox.StandardButton.Yes:
            return
        try:
            QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor)
            import ee
            ee.Authenticate()
        except Exception as e:                    # noqa: BLE001 - reported
            QMessageBox.critical(self, "Authentication failed",
                                 gu.explain_ee_error(e))
        finally:
            QApplication.restoreOverrideCursor()
        self.refresh()

    def _open_console(self):
        QDesktopServices.openUrl(QUrl(gu.CONSOLE_URL))

    def _save_project(self):
        gu.set_ee_project_id(self.ed_project.text().strip())
        self.refresh()

    def _test(self):
        try:
            QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor)
            ok, msg = gu.ee_connection_test()
        finally:
            QApplication.restoreOverrideCursor()
        self.s4.set_state("ok" if ok else "bad", msg)
        if ok:
            self.lbl_overall.setText(
                "<span style='color:{}'>Earth Engine is connected. You can "
                "start with Tool 1.</span>".format(OK_COLOR))

    # ---------------------------------------------------- boundary source
    def _load_source_settings(self):
        from .. import admin_sources
        kind, folder = admin_sources.saved_source()
        self.cb_src.setCurrentIndex(1 if kind == "custom" else 0)
        self.ed_folder.setText(folder)
        self._src_changed()
        self._describe_active()

    def _src_changed(self):
        custom = self.cb_src.currentIndex() == 1
        self.ed_folder.setEnabled(custom)

    def _browse_folder(self):
        d = QFileDialog.getExistingDirectory(
            self, "Folder with admin boundary files",
            self.ed_folder.text() or os.path.expanduser("~"))
        if d:
            self.ed_folder.setText(d)
            self._preview_folder(d)

    def _preview_folder(self, folder):
        from .. import admin_sources
        _admin, _lv, _lb, report = admin_sources.build_config(folder)
        self.txt_src.setPlainText("Detected:\n" + report)

    def _apply_source(self):
        from .. import adminmap, admin_sources
        custom = self.cb_src.currentIndex() == 1
        folder = self.ed_folder.text().strip()
        if custom and not folder:
            QMessageBox.information(self, "Boundary data",
                                    "Choose a folder first.")
            return
        ok, report = adminmap.set_admin_source(
            "custom" if custom else "bundled", folder)
        if not ok:
            self.txt_src.setPlainText(report)
            QMessageBox.warning(
                self, "Boundary data",
                "Could not use that source:\n\n" + report)
            return
        admin_sources.save_source("custom" if custom else "bundled", folder)
        self.txt_src.setPlainText("Active:\n" + report)
        QMessageBox.information(
            self, "Boundary data",
            "Boundary source applied.\n\nReopen the Map Layout tab so it "
            "picks up the new levels.")

    def _describe_active(self):
        from .. import adminmap
        try:
            lines = []
            for key in adminmap.ADMIN_LEVELS:
                shp, nf, cf, pf = adminmap.ACTIVE_ADMIN[key]
                lines.append("{:<22} {}   name='{}' code='{}' parent={}"
                             .format(adminmap.LEVEL_LABELS.get(key, key),
                                     shp, nf, cf, pf or "-"))
            self.txt_src.setPlainText(
                "Active source: {}\nFolder: {}\n\n{}".format(
                    adminmap.ADMIN_SOURCE, adminmap.admin_dir(),
                    "\n".join(lines)))
        except (AttributeError, KeyError, TypeError):  # partial config
            pass


class SetupWindow(QDialog):
    """Standalone window hosting the setup panel.

    Setup is a once-per-machine task, so it lives in the plugin menu rather
    than in the tool window or on the toolbar.
    """

    def __init__(self, iface, parent=None):
        super().__init__(parent)
        self.setWindowTitle("GEE LULC Toolkit - Setup")
        self.setWindowFlags(Qt.WindowType.Window
                            | Qt.WindowType.WindowMinMaxButtonsHint
                            | Qt.WindowType.WindowCloseButtonHint)
        self.resize(780, 740)
        v = QVBoxLayout(self)
        v.setContentsMargins(8, 8, 8, 8)
        self.panel = SetupDialog(iface, self)
        v.addWidget(self.panel, 1)
        h = QHBoxLayout()
        h.addStretch(1)
        btn = QPushButton("Close")
        btn.clicked.connect(self.close)
        h.addWidget(btn)
        v.addLayout(h)
