from __future__ import annotations

import json
import os
import subprocess
import sys
from datetime import datetime, timezone
from pathlib import Path
from typing import Any


def is_on_wifi() -> bool:
    override = os.environ.get("NIANNIAN_SYNC_WIFI")
    if override is not None:
        return override == "1"
    if sys.platform != "darwin":
        return True
    try:
        out = subprocess.run(
            ["/usr/sbin/networksetup", "-getairportnetwork", "en0"],
            capture_output=True,
            text=True,
            timeout=3,
            check=False,
        )
        text = out.stdout.lower()
        if "not associated" in text or "you are not associated" in text:
            return False
        return out.returncode == 0 and "current wi-fi network" in text
    except (OSError, subprocess.SubprocessError):
        return False


def is_ac_power() -> bool:
    override = os.environ.get("NIANNIAN_SYNC_AC")
    if override is not None:
        return override == "1"
    if sys.platform != "darwin":
        return True
    try:
        out = subprocess.run(
            ["pmset", "-g", "bat"],
            capture_output=True,
            text=True,
            timeout=3,
            check=False,
        )
        text = out.stdout.lower()
        if "no battery" in text:
            return True
        return "ac power" in text or "now drawing from 'ac power'" in text
    except (OSError, subprocess.SubprocessError):
        return True


def parse_iso(ts: str | None) -> datetime | None:
    if not ts:
        return None
    try:
        return datetime.fromisoformat(ts.replace("Z", "+00:00"))
    except ValueError:
        return None


def evaluate_push_conditions(
    *,
    sync_cfg: dict[str, Any],
    pending_atoms: int,
    pending_bytes: int,
    last_sync: str | None,
    force: bool = False,
) -> tuple[bool, list[str]]:
    if force:
        return True, []

    push_cfg = sync_cfg.get("push_to_cloud") or {}
    blocked: list[str] = []

    if push_cfg.get("require_wifi", True) and not is_on_wifi():
        blocked.append("require_wifi")
    if push_cfg.get("require_not_low_power", True) and not is_ac_power():
        blocked.append("require_not_low_power")

    if blocked:
        return False, blocked

    min_atoms = int(push_cfg.get("min_unsynced_atoms", 10))
    min_bytes = int(push_cfg.get("min_unsynced_bytes", 512000))
    max_hours = float(push_cfg.get("max_interval_hours", 24))

    threshold_met = pending_atoms >= min_atoms or pending_bytes >= min_bytes
    interval_met = False
    last = parse_iso(last_sync)
    if last is not None:
        elapsed = datetime.now(timezone.utc) - last.astimezone(timezone.utc)
        interval_met = elapsed.total_seconds() >= max_hours * 3600
    elif last_sync is None:
        interval_met = pending_atoms > 0 or pending_bytes > 0

    if threshold_met or interval_met:
        return True, []

    blocked.append("threshold_not_met")
    return False, blocked
