"""Record editor controller — drives the Add/Edit record dialog."""

import logging

from PyQt6.QtWidgets import QDialog

from dnsctl.core.state_manager import load_protected_records
from dnsctl.core.validations import validate_record

logger = logging.getLogger(__name__)

# Types that support the Proxied toggle
_PROXY_TYPES = {"A", "AAAA", "CNAME"}
# Types that use the Priority field
_PRIORITY_TYPES = {"MX", "SRV"}



class RecordEditorController:
    """Drives the record editor dialog for add / edit.

    After dialog.exec(), check ``.result`` for the record dict, or
    ``None`` if cancelled.
    """

    def __init__(self, dialog: QDialog, zone_name: str,
                 existing: dict | None = None) -> None:
        self._dialog = dialog
        self._zone_name = zone_name
        self._existing = existing  # None = add, dict = edit
        self._result: dict | None = None
        self._protect_changed: tuple[bool, bool, str] | None = None
        self._was_protected = False

    @property
    def result(self) -> dict | None:
        return self._result

    @property
    def protect_changed(self) -> tuple[bool, bool, str] | None:
        """Return (was_protected, is_protected, reason) if protect state changed, else None."""
        return self._protect_changed

    def setup(self) -> None:
        d = self._dialog

        if self._existing:
            d.setWindowTitle("Edit DNS Record")
        else:
            d.setWindowTitle("Add DNS Record")

        # Wire type-change to show/hide contextual fields
        d.typeCombo.currentTextChanged.connect(self._on_type_changed)
        d.saveButton.clicked.connect(self._on_save)
        d.cancelButton.clicked.connect(d.reject)

        # Icons and error label styling
        from dnsctl.gui.icons import get_icon
        from dnsctl.gui.theme import SEMANTIC_COLORS, load_theme_pref
        _colors = SEMANTIC_COLORS[load_theme_pref()]
        d.saveButton.setIcon(get_icon("save"))
        d.cancelButton.setIcon(get_icon("cancel"))
        d.errorLabel.setStyleSheet(f"color: {_colors['error']};")

        from dnsctl.gui.theme import ACCENT_COLOR
        from dnsctl.gui.hover_anim import install_hover_animation
        _accent = ACCENT_COLOR[load_theme_pref()]
        install_hover_animation(d.saveButton, color=_accent)
        install_hover_animation(d.cancelButton, color=_accent)

        # Wire protected checkbox to show/hide reason field
        d.protectedCheck.toggled.connect(self._on_protected_toggled)

        # Pre-populate for edit mode
        if self._existing:
            self._populate_from_record(self._existing)
            # Don't allow changing type when editing
            d.typeCombo.setEnabled(False)
        else:
            # Hide reason field initially
            d.reasonLabel.setVisible(False)
            d.reasonEdit.setVisible(False)
            self._on_type_changed(d.typeCombo.currentText())

    def _on_type_changed(self, rtype: str) -> None:
        d = self._dialog
        show_proxy = rtype in _PROXY_TYPES
        show_priority = rtype in _PRIORITY_TYPES

        d.proxiedLabel.setVisible(show_proxy)
        d.proxiedCheck.setVisible(show_proxy)
        d.priorityLabel.setVisible(show_priority)
        d.prioritySpin.setVisible(show_priority)

        # Update placeholder hints
        hints = {
            "A": "IPv4 address (e.g. 1.2.3.4)",
            "AAAA": "IPv6 address (e.g. 2001:db8::1)",
            "CNAME": "Target hostname (e.g. other.example.com)",
            "MX": "Mail server (e.g. mail.example.com)",
            "TXT": "Text value (e.g. v=spf1 include:...)",
            "SRV": "Target (e.g. sip.example.com)",
        }
        d.contentEdit.setPlaceholderText(hints.get(rtype, ""))

    def _on_protected_toggled(self, checked: bool) -> None:
        self._dialog.reasonLabel.setVisible(checked)
        self._dialog.reasonEdit.setVisible(checked)

    def _populate_from_record(self, rec: dict) -> None:
        d = self._dialog
        # Type
        idx = d.typeCombo.findText(rec.get("type", "A"))
        if idx >= 0:
            d.typeCombo.setCurrentIndex(idx)
        self._on_type_changed(rec.get("type", "A"))

        d.nameEdit.setText(rec.get("name", ""))
        d.contentEdit.setText(rec.get("content", ""))
        d.ttlSpin.setValue(rec.get("ttl", 1))
        d.proxiedCheck.setChecked(rec.get("proxied", False))
        d.prioritySpin.setValue(rec.get("priority", 10))

        # Check if record is currently protected
        rtype = rec.get("type", "")
        rname = rec.get("name", "")
        protected = load_protected_records()
        self._was_protected = any(
            p.get("type") == rtype and p.get("name") == rname for p in protected
        )
        d.protectedCheck.setChecked(self._was_protected)
        # Find existing reason
        reason = ""
        for p in protected:
            if p.get("type") == rtype and p.get("name") == rname:
                reason = p.get("reason", "")
                break
        d.reasonEdit.setText(reason)
        self._on_protected_toggled(self._was_protected)

    def _on_save(self) -> None:
        d = self._dialog
        d.errorLabel.setText("")

        rtype = d.typeCombo.currentText()
        name = d.nameEdit.text().strip()
        content = d.contentEdit.text().strip()

        # Auto-append zone name if name is not already a full hostname for this zone.
        # If the name contains dots but doesn't end with the zone (looks like an external
        # FQDN), warn the user rather than silently appending.
        if name and not name.endswith(self._zone_name) and not name.endswith("."):
            if "." not in name:
                name = f"{name}.{self._zone_name}"
            else:
                from PyQt6.QtWidgets import QMessageBox
                proposed = f"{name}.{self._zone_name}"
                answer = QMessageBox.question(
                    d,
                    "Confirm Record Name",
                    f"'{name}' doesn't end with the zone '{self._zone_name}'.\n\n"
                    f"Append the zone to make it '{proposed}'?\n\n"
                    f"Choose 'No' to keep '{name}' as-is.",
                    QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
                    QMessageBox.StandardButton.Yes,
                )
                if answer == QMessageBox.StandardButton.Yes:
                    name = proposed

        record: dict = {
            "type": rtype,
            "name": name,
            "content": content,
            "ttl": d.ttlSpin.value(),
            "proxied": d.proxiedCheck.isChecked() if rtype in _PROXY_TYPES else False,
        }

        if rtype in _PRIORITY_TYPES:
            record["priority"] = d.prioritySpin.value()

        # Carry forward the Cloudflare record ID for edits
        if self._existing and "id" in self._existing:
            record["id"] = self._existing["id"]

        err = validate_record(record)
        if err:
            d.errorLabel.setText(err)
            return

        # Track protect state change
        is_protected = d.protectedCheck.isChecked()
        reason = d.reasonEdit.text().strip()
        if is_protected != self._was_protected:
            self._protect_changed = (self._was_protected, is_protected, reason)
        elif is_protected and self._was_protected:
            # Reason may have changed
            self._protect_changed = (True, True, reason)

        self._result = record
        d.accept()
