"""
SolarMon: solar dashboard (Victron, Growatt, EPEver, batteries) and temp-sensor API.
Uses MySQL shop_stats; replicates appearance of PHP SolarMon index.php.
Weather for System column from Open-Meteo (Columbia, MS).
"""
import json
import os
import pymysql
import urllib.error
import urllib.request
from datetime import datetime
from flask import Blueprint, render_template, request, Response

from config import (
    MYSQL_HOST,
    MYSQL_PORT,
    MYSQL_DATABASE,
    MYSQL_USER,
    MYSQL_PASSWORD,
)
try:
    from config import WEATHER_LAT, WEATHER_LON, WEATHER_CACHE_MINUTES
except ImportError:
    WEATHER_LAT = 31.25
    WEATHER_LON = -89.83
    WEATHER_CACHE_MINUTES = 30
from blueprints.ota import ensure_ota_tables, get_latest_version_str, version_less

# Blueprint with its own static folder (CSS/JS from PHP project)
_BP_DIR = os.path.dirname(os.path.abspath(__file__))
bp = Blueprint(
    "solarmon",
    __name__,
    static_folder=os.path.join(_BP_DIR, "solarmon_static"),
    static_url_path="/solarmon/static",
)


def _get_conn():
    return pymysql.connect(
        host=MYSQL_HOST,
        port=MYSQL_PORT,
        user=MYSQL_USER,
        password=MYSQL_PASSWORD,
        database=MYSQL_DATABASE,
        cursorclass=pymysql.cursors.DictCursor,
    )


def _float(row, key, default=0):
    if not row or key not in row or row[key] is None or row[key] == "":
        return default
    try:
        return float(row[key])
    except (TypeError, ValueError):
        return default


def _str(row, key, default=""):
    if not row or key not in row or row[key] is None:
        return default
    return str(row[key]).strip()


def _bat_voltage_bar_pct(bat):
    """
    Single-cell Li-ion: return bar fill 0–100 from voltage (2.5V–4.3V).
    Bar width = position in voltage range, not charge percentage.
    """
    if bat is None or (isinstance(bat, str) and not bat.strip()):
        return None
    try:
        v = float(bat)
    except (ValueError, TypeError):
        return None
    # Single cell Li-ion: 2.5V = empty (0% bar), 4.3V = full (100% bar)
    if 2.5 <= v <= 4.3:
        return max(0, min(100, round((v - 2.5) / 1.8 * 100, 1)))
    return None


def _human_timedelta(dt, now):
    """
    Return a short, human-readable delta like '5 minutes ago', '2 hours ago', '3 days ago'.
    """
    try:
        delta = now - dt
    except Exception:
        return ""

    seconds = int(delta.total_seconds())
    if seconds < 0:
        seconds = 0

    minutes = seconds // 60
    hours = minutes // 60
    days = hours // 24

    if seconds < 60:
        return "just now"
    if minutes < 60:
        return f"{minutes} minute{'s' if minutes != 1 else ''} ago"
    if hours < 24:
        return f"{hours} hour{'s' if hours != 1 else ''} ago"
    return f"{days} day{'s' if days != 1 else ''} ago"


def _ota_latest_version_by_slug(conn, slug):
    """Return latest firmware version string for OTA project slug, or None."""
    try:
        ensure_ota_tables(conn)
        with conn.cursor() as cur:
            cur.execute("SELECT id FROM ota_projects WHERE slug = %s", (slug,))
            row = cur.fetchone()
        if not row:
            return None
        return get_latest_version_str(conn, row["id"])
    except Exception:
        return None


# In-memory cache for Open-Meteo weather (Columbia, MS)
_weather_cache = None
_weather_cache_time = None


def _wmo_weathercode_to_conditions(code):
    """Map WMO weather code to short condition string for display."""
    if code is None:
        return ""
    c = int(code)
    if c == 0:
        return "Clear"
    if c in (1, 2, 3):
        return "Partly cloudy" if c <= 2 else "Overcast"
    if c in (45, 48):
        return "Fog"
    if 51 <= c <= 67:
        return "Rain" if c >= 61 else "Drizzle"
    if 71 <= c <= 77:
        return "Snow"
    if 80 <= c <= 82:
        return "Showers"
    if 85 <= c <= 86:
        return "Snow showers"
    if 95 <= c <= 99:
        return "Thunderstorms"
    return ""


def _fetch_weather_open_meteo():
    """
    Fetch today/tomorrow weather for Columbia, MS from Open-Meteo.
    Returns dict with w_conditions, w_temp, w_tempmax, w_tempmin, wt_conditions, wt_tempmax, wt_tempmin
    or None on failure.
    """
    url = (
        "https://api.open-meteo.com/v1/forecast?"
        "latitude={:f}&longitude={:f}&timezone=America/Chicago&"
        "current_weather=true&temperature_unit=fahrenheit&daily=temperature_2m_max,temperature_2m_min,weathercode"
    ).format(WEATHER_LAT, WEATHER_LON)
    try:
        req = urllib.request.Request(url, headers={"User-Agent": "SolarMon/1.0"})
        with urllib.request.urlopen(req, timeout=10) as resp:
            data = json.loads(resp.read().decode())
    except (urllib.error.URLError, urllib.error.HTTPError, json.JSONDecodeError, OSError):
        return None
    try:
        cw = data.get("current_weather") or {}
        daily = data.get("daily") or {}
        maxes = daily.get("temperature_2m_max") or []
        mins = daily.get("temperature_2m_min") or []
        codes = daily.get("weathercode") or []
        out = {}
        # Today
        out["w_temp"] = str(round(cw.get("temperature", 0), 1)) if cw.get("temperature") is not None else ""
        out["w_tempmax"] = str(round(maxes[0], 1)) if len(maxes) > 0 and maxes[0] is not None else ""
        out["w_tempmin"] = str(round(mins[0], 1)) if len(mins) > 0 and mins[0] is not None else ""
        out["w_conditions"] = _wmo_weathercode_to_conditions(codes[0] if len(codes) > 0 else None)
        out["w_icon"] = ""
        # Tomorrow
        out["wt_tempmax"] = str(round(maxes[1], 1)) if len(maxes) > 1 and maxes[1] is not None else ""
        out["wt_tempmin"] = str(round(mins[1], 1)) if len(mins) > 1 and mins[1] is not None else ""
        out["wt_temp"] = ""
        out["wt_conditions"] = _wmo_weathercode_to_conditions(codes[1] if len(codes) > 1 else None)
        out["wt_icon"] = ""
        return out
    except (IndexError, TypeError, ValueError):
        return None


def _get_weather_cached():
    """Return cached weather or fetch and cache. Returns dict or None."""
    global _weather_cache, _weather_cache_time
    now = datetime.now()
    cache_mins = int(WEATHER_CACHE_MINUTES) if WEATHER_CACHE_MINUTES is not None else 30
    if _weather_cache is not None and _weather_cache_time is not None:
        delta = (now - _weather_cache_time).total_seconds()
        if delta < cache_mins * 60:
            return _weather_cache
    weather = _fetch_weather_open_meteo()
    if weather is not None:
        _weather_cache = weather
        _weather_cache_time = now
    return weather


def fetch_dashboard_data():
    """Fetch all data for the main dashboard; return a dict with defaults for missing data."""
    defaults = {
        "w_tempmin": "",
        "w_tempmax": "",
        "w_temp": "",
        "w_conditions": "",
        "w_icon": "",
        "wt_tempmin": "",
        "wt_tempmax": "",
        "wt_temp": "",
        "wt_conditions": "",
        "wt_icon": "",
        "vic_voltage": 0.0,
        "current": 0,
        "vic_soc": 0,
        "vic_ttg": "No Record",
        "Gw1P": 0.0,
        "Gw2P": 0.0,
        "Gw3P": 0.0,
        "EP1P": 0.0,
        "EP2P": 0.0,
        "solar_power": 0.0,
        "Gw1C": 0.0,
        "Gw2C": 0.0,
        "Gw3C": 0.0,
        "EP1C": 0.0,
        "EP2C": 0.0,
        "total_current": 0.0,
        "Gw1Load": 0,
        "Gw2Load": 0,
        "Gw3Load": 0,
        "total_inv_current": 0.0,
        "batteries": [],
        "shed_temp": "",
        "shed_humid": "",
        "up_time": "",
        "date": "",
        "users": "",
        "tasks": "",
        "consumption": 0.0,
        "temps_latest": [],
        "moisture_latest": [],
        "pump_latest": None,
        "wifiswitch_latest": None,
    }
    try:
        conn = _get_conn()
    except pymysql.Error:
        return defaults

    try:
        # OTA: latest version per project slug for "newer available" in firmware cells
        ota_latest = {}
        for slug in ("wifitemps", "moisturemeter", "pumpcontrol", "wifiswitch"):
            ota_latest[slug] = _ota_latest_version_by_slug(conn, slug)

        # Weather: Columbia, MS from Open-Meteo (cached)
        weather = _get_weather_cached()
        if weather:
            defaults["w_conditions"] = weather.get("w_conditions", "")
            defaults["w_temp"] = weather.get("w_temp", "")
            defaults["w_tempmax"] = weather.get("w_tempmax", "")
            defaults["w_tempmin"] = weather.get("w_tempmin", "")
            defaults["w_icon"] = weather.get("w_icon", "")
            defaults["wt_conditions"] = weather.get("wt_conditions", "")
            defaults["wt_tempmax"] = weather.get("wt_tempmax", "")
            defaults["wt_tempmin"] = weather.get("wt_tempmin", "")
            defaults["wt_temp"] = weather.get("wt_temp", "")
            defaults["wt_icon"] = weather.get("wt_icon", "")

        # VEDirect Victron 2
        with conn.cursor() as cur:
            cur.execute(
                "SELECT V, P, I, SOC, TTG FROM VEDirect "
                "WHERE Name = %s AND LastUpdate >= DATE_SUB(NOW(), INTERVAL 5 MINUTE) "
                "ORDER BY id DESC LIMIT 1",
                ("Victron 2",),
            )
            row = cur.fetchone()
            if row:
                v = _float(row, "V")
                defaults["vic_voltage"] = v * 0.001 if v else 0.0
                defaults["current"] = int(_float(row, "I"))
                defaults["vic_soc"] = int(_float(row, "SOC"))
                ttg = row.get("TTG")
                if ttg is not None and ttg != "":
                    try:
                        ttg = float(ttg)
                        if ttg < 0:
                            defaults["vic_ttg"] = "Infinate"
                        else:
                            days = round(ttg / 24 / 60)
                            hours = round((ttg - round(days * 24 * 60)) / 60)
                            minutes = round(
                                (ttg - (((days * 24 * 60) / 60) + (hours * 60))) / 60
                            )
                            defaults["vic_ttg"] = (
                                f"{days} Days {hours} Hrs {minutes} Min"
                            )
                    except (TypeError, ValueError):
                        pass

        # Growatt/EPEver power (Gw1P..EP2P, solar_power)
        with conn.cursor() as cur:
            cur.execute(
                """
                SELECT
                  (SELECT ((((Ppv1H * 10) << 16) + (Ppv1L * 10)) * .1) FROM Growatt
                   WHERE Name = 'Growatt 1' AND LastUpdate >= DATE_SUB(NOW(), INTERVAL 5 MINUTE) ORDER BY id DESC LIMIT 1) AS Gw1P,
                  (SELECT ((((Ppv1H * 10) << 16) + (Ppv1L * 10)) * .1) FROM Growatt
                   WHERE Name = 'Growatt 2' AND LastUpdate >= DATE_SUB(NOW(), INTERVAL 5 MINUTE) ORDER BY id DESC LIMIT 1) AS Gw2P,
                  (SELECT ((((Ppv1H * 10) << 16) + (Ppv1L * 10)) * .1) FROM Growatt
                   WHERE Name = 'Growatt 3' AND LastUpdate >= DATE_SUB(NOW(), INTERVAL 5 MINUTE) ORDER BY id DESC LIMIT 1) AS Gw3P,
                  (SELECT ((((r3103 * 10) << 16) + (r3102 * 10)) * .001) FROM EPEver
                   WHERE Name = 'Epever 01' AND LastUpdate >= DATE_SUB(NOW(), INTERVAL 5 MINUTE) ORDER BY id DESC LIMIT 1) AS EP1P,
                  (SELECT ((((r3103 * 10) << 16) + (r3102 * 10)) * .001) FROM EPEver
                   WHERE Name = 'Epever 02' AND LastUpdate >= DATE_SUB(NOW(), INTERVAL 5 MINUTE) ORDER BY id DESC LIMIT 1) AS EP2P
                """
            )
            row = cur.fetchone()
            if row:
                gw1 = _float(row, "Gw1P")
                gw2 = _float(row, "Gw2P")
                gw3 = _float(row, "Gw3P")
                ep1 = _float(row, "EP1P")
                ep2 = _float(row, "EP2P")
                defaults["Gw1P"] = gw1
                defaults["Gw2P"] = gw2
                defaults["Gw3P"] = gw3
                defaults["EP1P"] = ep1
                defaults["EP2P"] = ep2
                defaults["solar_power"] = gw1 + gw2 + gw3 + ep1 + ep2

        # Buck current (total_current from Growatt + EPEver)
        with conn.cursor() as cur:
            cur.execute(
                """
                SELECT
                  (SELECT Buck1Curr FROM Growatt WHERE Name = 'Growatt 1' AND LastUpdate >= DATE_SUB(NOW(), INTERVAL 5 MINUTE) ORDER BY id DESC LIMIT 1) AS Gw1C,
                  (SELECT Buck1Curr FROM Growatt WHERE Name = 'Growatt 2' AND LastUpdate >= DATE_SUB(NOW(), INTERVAL 5 MINUTE) ORDER BY id DESC LIMIT 1) AS Gw2C,
                  (SELECT Buck1Curr FROM Growatt WHERE Name = 'Growatt 3' AND LastUpdate >= DATE_SUB(NOW(), INTERVAL 5 MINUTE) ORDER BY id DESC LIMIT 1) AS Gw3C,
                  (SELECT (r3105 * .01) FROM EPEver WHERE Name = 'Epever 01' AND LastUpdate >= DATE_SUB(NOW(), INTERVAL 5 MINUTE) ORDER BY id DESC LIMIT 1) AS EP1C,
                  (SELECT (r3105 * .01) FROM EPEver WHERE Name = 'Epever 02' AND LastUpdate >= DATE_SUB(NOW(), INTERVAL 5 MINUTE) ORDER BY id DESC LIMIT 1) AS EP2C
                """
            )
            row = cur.fetchone()
            if row:
                c1 = _float(row, "Gw1C")
                c2 = _float(row, "Gw2C")
                c3 = _float(row, "Gw3C")
                c4 = _float(row, "EP1C")
                c5 = _float(row, "EP2C")
                defaults["total_current"] = c1 + c2 + c3 + c4 + c5

        # Inv_Curr (total_inv_current)
        with conn.cursor() as cur:
            cur.execute(
                """
                SELECT
                  (SELECT Inv_Curr FROM Growatt WHERE Name = 'Growatt 1' AND LastUpdate >= DATE_SUB(NOW(), INTERVAL 5 MINUTE) ORDER BY id DESC LIMIT 1) AS Gw1C,
                  (SELECT Inv_Curr FROM Growatt WHERE Name = 'Growatt 2' AND LastUpdate >= DATE_SUB(NOW(), INTERVAL 5 MINUTE) ORDER BY id DESC LIMIT 1) AS Gw2C,
                  (SELECT Inv_Curr FROM Growatt WHERE Name = 'Growatt 3' AND LastUpdate >= DATE_SUB(NOW(), INTERVAL 5 MINUTE) ORDER BY id DESC LIMIT 1) AS Gw3C
                """
            )
            row = cur.fetchone()
            if row:
                defaults["total_inv_current"] = (
                    _float(row, "Gw1C") + _float(row, "Gw2C") + _float(row, "Gw3C")
                )

        # Battery: latest row per Name (last 5 min), sorted by Name
        def _bat_status(c):
            if c == 0:
                return "Equalized"
            if c > 0:
                return f"Char: {round(c, 2)}a"
            return f"Disc: {round(c, 2)}a"

        with conn.cursor() as cur:
            cur.execute(
                """
                SELECT b.Name, b.PercentCapacity, b.Voltage, b.Current
                FROM Battery b
                INNER JOIN (
                    SELECT Name, MAX(id) AS max_id
                    FROM Battery
                    WHERE LastUpdate >= DATE_SUB(NOW(), INTERVAL 5 MINUTE)
                    GROUP BY Name
                ) t ON b.Name = t.Name AND b.id = t.max_id
                ORDER BY b.Name
                """
            )
            rows = cur.fetchall()
            batteries = []
            for row in rows:
                name = _str(row, "Name")
                current = _float(row, "Current")
                batteries.append({
                    "name": name,
                    "soc": int(_float(row, "PercentCapacity")),
                    "voltage": _float(row, "Voltage"),
                    "current": current,
                    "status": _bat_status(current),
                })
            defaults["batteries"] = batteries

        # Environment
        with conn.cursor() as cur:
            cur.execute(
                "SELECT shed_Temperature AS shed_temp, shed_Humiditity AS shed_humiditity, "
                "`up`, `date`, users, tasks FROM Environment "
                "WHERE LastUpdate >= DATE_SUB(NOW(), INTERVAL 5 MINUTE) AND shed_Humiditity <> '' "
                "ORDER BY id DESC LIMIT 1"
            )
            row = cur.fetchone()
            if row:
                # Shed temp: DB may use alias or column name; value may be "22.5" or "22.5 C"
                raw_temp = (
                    row.get("shed_Temperature")
                    or row.get("shed_temp")
                    or ""
                )
                if isinstance(raw_temp, (int, float)):
                    raw_temp = str(raw_temp)
                else:
                    raw_temp = str(raw_temp).strip()
                # Strip trailing C/c/°C so we can parse the number (e.g. "29.93C", "22.5 C")
                for suffix in (" °C", "°C", " C", "C", "c"):
                    if raw_temp.endswith(suffix):
                        raw_temp = raw_temp[: -len(suffix)].strip()
                        break
                try:
                    c = float(raw_temp)
                    defaults["shed_temp"] = f"{(c * 9 / 5) + 32:.1f} °F"
                except (ValueError, TypeError):
                    defaults["shed_temp"] = raw_temp or ""
                defaults["shed_humid"] = _str(row, "shed_humiditity")
                up = _str(row, "up")
                up = up.replace("up ", "").replace("hours", "h").replace("minutes", "m")
                defaults["up_time"] = up
                defaults["date"] = _str(row, "date")
                defaults["users"] = _str(row, "users")
                defaults["tasks"] = _str(row, "tasks")

        # Temps: latest record per unit_id (same as Temps app)
        with conn.cursor() as cur:
            try:
                cur.execute("ALTER TABLE Temps ADD COLUMN firmware_version VARCHAR(24) DEFAULT NULL")
            except pymysql.OperationalError:
                pass  # column already exists
            cur.execute(
                """
                SELECT t.unit_id, t.node_name, t.temp, t.humid, t.bat, t.entry_date, t.firmware_version
                FROM Temps t
                INNER JOIN (
                    SELECT unit_id, MAX(entry_date) AS max_entry_date
                    FROM Temps
                        WHERE entry_date >= NOW() - INTERVAL 20 MINUTE
                    GROUP BY unit_id
                ) latest ON t.unit_id = latest.unit_id AND t.entry_date = latest.max_entry_date
                    WHERE t.entry_date >= NOW() - INTERVAL 20 MINUTE
                ORDER BY t.unit_id
                """
            )
            rows = cur.fetchall()
            now = datetime.now()
            ota_temps = ota_latest.get("wifitemps")
            for r in rows:
                r["bat_bar_pct"] = _bat_voltage_bar_pct(r.get("bat"))
                # Format voltage for display (single cell)
                b = r.get("bat")
                if b is not None and b != "":
                    try:
                        r["bat_display"] = f"{float(b):.2f} V"
                    except (ValueError, TypeError):
                        r["bat_display"] = str(b)
                else:
                    r["bat_display"] = ""
                entry_dt = r.get("entry_date")
                if entry_dt:
                    r["last_seen_ago"] = _human_timedelta(entry_dt, now)
                else:
                    r["last_seen_ago"] = ""
                r["ota_newer_available"] = bool(
                    ota_temps and r.get("firmware_version") and version_less(r["firmware_version"], ota_temps)
                )
                r["ota_latest_version"] = ota_temps
                r["ota_project_slug"] = "wifitemps"
            defaults["temps_latest"] = rows

        # Moisture: latest reading per device_id (same as Moisture app grid)
        try:
            with conn.cursor() as cur:
                try:
                    cur.execute("ALTER TABLE MoistureLevel ADD COLUMN firmware_version VARCHAR(24) DEFAULT NULL")
                except pymysql.OperationalError:
                    pass  # column already exists or table doesn't exist
                cur.execute(
                    """
                    SELECT m.device_id, m.node_name, m.moisture, m.battery_voltage, m.command, m.firmware_version, m.created_at
                    FROM MoistureLevel m
                    INNER JOIN (
                        SELECT device_id, MAX(id) AS max_id FROM MoistureLevel GROUP BY device_id
                    ) latest ON m.device_id = latest.device_id AND m.id = latest.max_id
                    WHERE m.created_at >= NOW() - INTERVAL 20 MINUTE
                    ORDER BY m.device_id
                    """
                )
                rows = cur.fetchall()
                now = datetime.now()
                ota_moisture = ota_latest.get("moisturemeter")
                for r in rows:
                    r["bat_bar_pct"] = _bat_voltage_bar_pct(r.get("battery_voltage"))
                    b = r.get("battery_voltage")
                    if b is not None and b != "":
                        try:
                            r["bat_display"] = f"{float(b):.2f} V"
                        except (ValueError, TypeError):
                            r["bat_display"] = str(b)
                    else:
                        r["bat_display"] = ""
                    # Stale if older than 30 minutes (local time)
                    created = r.get("created_at")
                    if created:
                        r["is_stale"] = (now - created).total_seconds() > 30 * 60
                        r["last_seen_ago"] = _human_timedelta(created, now)
                    else:
                        r["is_stale"] = False
                        r["last_seen_ago"] = ""
                    r["ota_newer_available"] = bool(
                        ota_moisture and r.get("firmware_version") and version_less(r["firmware_version"], ota_moisture)
                    )
                    r["ota_latest_version"] = ota_moisture
                    r["ota_project_slug"] = "moisturemeter"
                defaults["moisture_latest"] = rows
        except pymysql.OperationalError:
            # MoistureLevel table may not exist yet
            pass

        # PumpControl: latest PumpStatus record
        try:
            with conn.cursor() as cur:
                cur.execute(
                    "SELECT id, pressure_psi, pump_on, error_pressure_low, app_state, rssi, ip, mac, firmware_version, created_at "
                    "FROM PumpStatus ORDER BY created_at DESC LIMIT 1"
                )
                row = cur.fetchone()
                if row:
                    if row.get("created_at"):
                        row = dict(row)
                        now = datetime.now()
                        row["last_seen_ago"] = _human_timedelta(row["created_at"], now)
                    ota_pump = ota_latest.get("pumpcontrol")
                    row["ota_newer_available"] = bool(
                        ota_pump and row.get("firmware_version") and version_less(row["firmware_version"], ota_pump)
                    )
                    row["ota_latest_version"] = ota_pump
                    row["ota_project_slug"] = "pumpcontrol"
                    defaults["pump_latest"] = row
        except (pymysql.OperationalError, pymysql.ProgrammingError):
            # PumpStatus table may not exist yet (1146)
            pass

        # WifiSwitch: latest WifiSwitchStatus record
        try:
            with conn.cursor() as cur:
                cur.execute(
                    "SELECT id, unit_id, node_name, temp, humid, relay_on, temp_rule_enabled, mac, ip, firmware_version, created_at "
                    "FROM WifiSwitchStatus ORDER BY created_at DESC LIMIT 1"
                )
                row = cur.fetchone()
                if row:
                    if row.get("created_at"):
                        row = dict(row)
                        now = datetime.now()
                        row["last_seen_ago"] = _human_timedelta(row["created_at"], now)
                    # Convert TINYINT -> bool for display
                    if row.get("relay_on") is not None:
                        row["relay_on"] = bool(row.get("relay_on"))
                    if row.get("temp_rule_enabled") is not None:
                        row["temp_rule_enabled"] = bool(row.get("temp_rule_enabled"))
                    ota_switch = ota_latest.get("wifiswitch")
                    row["ota_newer_available"] = bool(
                        ota_switch and row.get("firmware_version") and version_less(row["firmware_version"], ota_switch)
                    )
                    row["ota_latest_version"] = ota_switch
                    row["ota_project_slug"] = "wifiswitch"
                    defaults["wifiswitch_latest"] = row
        except (pymysql.OperationalError, pymysql.ProgrammingError):
            # WifiSwitchStatus table may not exist yet
            pass

    finally:
        conn.close()

    # Consumption = total_current (solar) - Victron current (mA -> A)
    try:
        defaults["consumption"] = round(
            defaults["total_current"] - (defaults["current"] * 0.001), 2
        )
    except (TypeError, ValueError):
        defaults["consumption"] = 0.0

    return defaults


@bp.route("/")
def index():
    """Main SolarMon dashboard (same appearance as PHP index.php)."""
    data = fetch_dashboard_data()
    return render_template("solarmon/index.html", **data)


@bp.route("/moisture/delete", methods=["POST"])
def delete_moisture_device():
    """
    Delete all MoistureLevel records for a given device_id.
    Intended to be called from the SolarMon dashboard via AJAX.
    """
    payload = request.get_json(silent=True) or {}
    device_id = (payload.get("device_id") or "").strip()
    if not device_id:
        return Response(
            '{"error": "device_id required"}',
            status=400,
            mimetype="application/json",
        )

    try:
        conn = _get_conn()
    except pymysql.Error:
        return Response(
            '{"error": "database unavailable"}',
            status=500,
            mimetype="application/json",
        )

    try:
        with conn.cursor() as cur:
            cur.execute(
                "DELETE FROM MoistureLevel WHERE device_id = %s",
                (device_id,),
            )
        conn.commit()
        return Response('{"success": true}', mimetype="application/json")
    except pymysql.Error:
        conn.rollback()
        return Response(
            '{"error": "delete failed"}',
            status=500,
            mimetype="application/json",
        )
    finally:
        conn.close()


@bp.route("/temps", methods=["GET", "POST"])
def temps_api():
    """
    Temp-sensor API: accept id, temp, humid, mac, ip, bat, name.
    Insert reading; return '~' or '@Com:<command>~' (parameterized queries).
    """
    args = request.args if request.method == "GET" else request.form
    unit_id = args.get("id", "").strip()
    temp = args.get("temp", "").strip()
    humid = args.get("humid", "").strip()
    mac = args.get("mac", "").strip()
    ip = args.get("ip", "").strip()
    bat = args.get("bat", "").strip()
    node_name = args.get("name", "").strip()
    firmware_version = (args.get("firmware_version") or "").strip()[:24] or None
    entry_date = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")

    try:
        conn = _get_conn()
    except pymysql.Error:
        return Response("~", mimetype="text/plain")

    try:
        command = None
        with conn.cursor() as cur:
            cur.execute(
                "SELECT command FROM Temps WHERE unit_id = %s ORDER BY id DESC LIMIT 1",
                (unit_id,),
            )
            row = cur.fetchone()
            if row and row.get("command") and len(str(row["command"]).strip()) > 0:
                command = str(row["command"]).strip()

        with conn.cursor() as cur:
            cur.execute(
                "INSERT INTO Temps (unit_id, entry_date, temp, humid, mac, ip, bat, node_name, firmware_version) "
                "VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)",
                (unit_id, entry_date, temp, humid, mac, ip, bat, node_name, firmware_version),
            )
        conn.commit()

        if command:
            return Response(f"@Com:{command}~", mimetype="text/plain")
        return Response("~", mimetype="text/plain")
    except pymysql.Error:
        conn.rollback()
        return Response("~", mimetype="text/plain")
    finally:
        conn.close()
