from datetime import datetime

from sqlalchemy import Boolean, DateTime, Float, ForeignKey, Integer, String, Text, func
from sqlalchemy.orm import Mapped, mapped_column, relationship

from backend.app.core.database import Base


class SmartPlug(Base):
    """Smart plug for printer power control (Tasmota, Home Assistant, MQTT, or REST)."""

    __tablename__ = "smart_plugs"

    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String(100))
    ip_address: Mapped[str | None] = mapped_column(String(45), nullable=True)  # IPv4/IPv6 (required for Tasmota)

    # Plug type: "tasmota" (default), "homeassistant", "mqtt", or "rest"
    plug_type: Mapped[str] = mapped_column(String(20), default="tasmota")
    # Home Assistant entity ID (e.g., "switch.printer_plug")
    ha_entity_id: Mapped[str | None] = mapped_column(String(100), nullable=True)
    # Home Assistant energy sensor entities (optional, for separate energy sensors)
    ha_power_entity: Mapped[str | None] = mapped_column(String(100), nullable=True)  # sensor.xxx_power
    ha_energy_today_entity: Mapped[str | None] = mapped_column(String(100), nullable=True)  # sensor.xxx_today
    ha_energy_total_entity: Mapped[str | None] = mapped_column(String(100), nullable=True)  # sensor.xxx_total

    # MQTT plug fields (required when plug_type="mqtt")
    # Legacy field - kept for backward compatibility, now use mqtt_power_topic
    mqtt_topic: Mapped[str | None] = mapped_column(
        String(200), nullable=True
    )  # e.g., "zigbee2mqtt/shelly-working-room" (deprecated, use mqtt_power_topic)

    # Power monitoring
    mqtt_power_topic: Mapped[str | None] = mapped_column(String(200), nullable=True)  # Topic for power data
    mqtt_power_path: Mapped[str | None] = mapped_column(String(100), nullable=True)  # e.g., "power_l1" or "data.power"
    mqtt_power_multiplier: Mapped[float] = mapped_column(Float, default=1.0)  # Unit conversion for power

    # Energy monitoring
    mqtt_energy_topic: Mapped[str | None] = mapped_column(String(200), nullable=True)  # Topic for energy data
    mqtt_energy_path: Mapped[str | None] = mapped_column(String(100), nullable=True)  # e.g., "energy_l1"
    mqtt_energy_multiplier: Mapped[float] = mapped_column(Float, default=1.0)  # Unit conversion for energy

    # State monitoring
    mqtt_state_topic: Mapped[str | None] = mapped_column(String(200), nullable=True)  # Topic for state data
    mqtt_state_path: Mapped[str | None] = mapped_column(String(100), nullable=True)  # e.g., "state_l1" for ON/OFF
    mqtt_state_on_value: Mapped[str | None] = mapped_column(
        String(50), nullable=True
    )  # What value means "ON" (e.g., "ON", "true", "1")

    # Legacy multiplier - kept for backward compatibility
    mqtt_multiplier: Mapped[float] = mapped_column(Float, default=1.0)  # Deprecated, use mqtt_power_multiplier

    # REST/Webhook plug fields (required when plug_type="rest")
    # Control URLs
    rest_on_url: Mapped[str | None] = mapped_column(String(500), nullable=True)  # Full URL to turn ON
    rest_on_body: Mapped[str | None] = mapped_column(Text, nullable=True)  # Request body for ON
    rest_off_url: Mapped[str | None] = mapped_column(String(500), nullable=True)  # Full URL to turn OFF
    rest_off_body: Mapped[str | None] = mapped_column(Text, nullable=True)  # Request body for OFF
    rest_method: Mapped[str | None] = mapped_column(String(10), nullable=True)  # HTTP method: POST, PUT, GET
    rest_headers: Mapped[str | None] = mapped_column(Text, nullable=True)  # JSON string of custom headers
    # Status polling (optional)
    rest_status_url: Mapped[str | None] = mapped_column(String(500), nullable=True)  # GET endpoint for state
    rest_status_path: Mapped[str | None] = mapped_column(String(200), nullable=True)  # JSON path to state value
    rest_status_on_value: Mapped[str | None] = mapped_column(String(50), nullable=True)  # What value means ON
    # Energy monitoring (optional — can use separate URLs or extract from status response)
    rest_power_url: Mapped[str | None] = mapped_column(String(500), nullable=True)  # Separate URL for power data
    rest_power_path: Mapped[str | None] = mapped_column(String(200), nullable=True)  # JSON path for power (watts)
    rest_power_multiplier: Mapped[float] = mapped_column(Float, server_default="1.0")  # Unit conversion for power
    rest_energy_url: Mapped[str | None] = mapped_column(String(500), nullable=True)  # Separate URL for energy data
    rest_energy_path: Mapped[str | None] = mapped_column(String(200), nullable=True)  # JSON path for energy (kWh)
    rest_energy_multiplier: Mapped[float] = mapped_column(
        Float, server_default="1.0"
    )  # Unit conversion (e.g., 0.001 for Wh→kWh)

    # Link to printer (multiple plugs/scripts can be linked to one printer)
    printer_id: Mapped[int | None] = mapped_column(ForeignKey("printers.id", ondelete="SET NULL"), nullable=True)

    # Automation settings
    enabled: Mapped[bool] = mapped_column(Boolean, default=True)
    auto_on: Mapped[bool] = mapped_column(Boolean, default=True)  # Turn on at print start
    auto_off: Mapped[bool] = mapped_column(Boolean, default=True)  # Turn off at print complete/fail
    auto_off_persistent: Mapped[bool] = mapped_column(Boolean, default=False)  # Keep auto-off enabled between prints

    # Turn-off delay mode: "time" or "temperature"
    off_delay_mode: Mapped[str] = mapped_column(String(20), default="time")
    off_delay_minutes: Mapped[int] = mapped_column(Integer, default=5)  # For time mode
    off_temp_threshold: Mapped[int] = mapped_column(Integer, default=70)  # For temp mode (°C)

    # Auto-off after AMS drying completes (#1349). Independent of `auto_off`
    # (which only fires after a print finishes). Uses its own delay because
    # the AMS is hot after a drying cycle and users may want longer cooldown
    # than the print-finish default. Fires whenever any AMS attached to the
    # linked printer finishes a dry cycle — Bambuddy doesn't model per-AMS
    # plug routing, the trigger is plug-vs-printer-level.
    auto_off_after_drying: Mapped[bool] = mapped_column(Boolean, default=False, server_default="0")
    off_delay_after_drying_minutes: Mapped[int] = mapped_column(Integer, default=10, server_default="10")

    # Optional auth (some Tasmota configs require it)
    username: Mapped[str | None] = mapped_column(String(50), nullable=True)
    password: Mapped[str | None] = mapped_column(String(100), nullable=True)

    # Power alerts
    power_alert_enabled: Mapped[bool] = mapped_column(Boolean, default=False)
    power_alert_high: Mapped[float | None] = mapped_column(Float, nullable=True)  # Alert when power > this (watts)
    power_alert_low: Mapped[float | None] = mapped_column(Float, nullable=True)  # Alert when power < this (watts)
    power_alert_last_triggered: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)  # Cooldown tracking

    # Schedule (time-based on/off)
    schedule_enabled: Mapped[bool] = mapped_column(Boolean, default=False)
    schedule_on_time: Mapped[str | None] = mapped_column(String(5), nullable=True)  # "HH:MM" format
    schedule_off_time: Mapped[str | None] = mapped_column(String(5), nullable=True)  # "HH:MM" format

    # Switchbar visibility
    show_in_switchbar: Mapped[bool] = mapped_column(Boolean, default=False)

    # Printer card visibility (for scripts)
    show_on_printer_card: Mapped[bool] = mapped_column(Boolean, default=True)

    # Status tracking
    last_state: Mapped[str | None] = mapped_column(String(10), nullable=True)  # "ON"/"OFF"
    last_checked: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
    auto_off_executed: Mapped[bool] = mapped_column(Boolean, default=False)  # True when auto-off was triggered
    auto_off_pending: Mapped[bool] = mapped_column(Boolean, default=False)  # True when waiting for cooldown
    auto_off_pending_since: Mapped[datetime | None] = mapped_column(
        DateTime, nullable=True
    )  # When auto-off was scheduled

    # Timestamps
    created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now())
    updated_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), onupdate=func.now())

    # Relationship
    printer: Mapped["Printer"] = relationship(back_populates="smart_plugs")


from backend.app.models.printer import Printer  # noqa: E402
