from pathlib import Path
import time
import os
import subprocess
from config import load_config
from logger import get_logger

log = get_logger("actions.cron")


def read_crontab() -> str:
    result = subprocess.run(["crontab", "-l"], capture_output=True, text=True, check=False)
    if result.returncode != 0:
        stderr = result.stderr.lower()
        if "no crontab for" in stderr or "no crontab" in stderr:
            return ""
        raise OSError(f"Failed to read crontab: {result.stderr.strip()}")
    return result.stdout


def write_crontab(content: str) -> None:
    import tempfile
    fd, temp_path = tempfile.mkstemp(prefix="cron_")
    try:
        with os.fdopen(fd, "w", encoding="utf-8") as temp_file:
            temp_file.write(content)
            if content and not content.endswith("\n"):
                temp_file.write("\n")

        result = subprocess.run(["crontab", temp_path], capture_output=True, text=True, check=False)
        if result.returncode != 0:
            raise OSError(f"Failed to write crontab: {result.stderr.strip()}")
    finally:
        try:
            os.remove(temp_path)
        except OSError:
            pass


def handle_cron_create(payload: dict, backup_dir: Path | None = None) -> dict:
    try:
        schedule_raw = str(payload.get("schedule", ""))
        command_raw = str(payload.get("command", ""))
        comment_raw = str(payload.get("comment", ""))

        # Security checks on raw values
        for name, val in [("schedule", schedule_raw), ("command", command_raw), ("comment", comment_raw)]:
            if "\n" in val or "\r" in val:
                raise ValueError(f"Newline character detected in {name}")

        schedule = schedule_raw.strip()
        command = command_raw.strip()
        comment = comment_raw.strip()

        if not schedule or not command or not comment:
            raise ValueError("schedule, command, and comment are required")

        if "#" in schedule or "#" in command:
            raise ValueError("Hash character (#) is not allowed in schedule or command")

        # Parse ID from comment
        if "wolfpanel-managed:" in comment:
            cron_id = comment.split("wolfpanel-managed:")[-1].strip()
        else:
            cron_id = comment.strip()

        if not cron_id:
            raise ValueError("Invalid cron_id resolved from comment")

        # Get backup dir
        if backup_dir is None:
            config = load_config()
            backup_dir = config.etc_dir / "backups"

        backup_dir.mkdir(parents=True, exist_ok=True)

        # Read current crontab
        current = read_crontab()

        # Backup current
        timestamp = int(time.time())
        backup_file = backup_dir / f"crontab_{timestamp}.bak"
        backup_file.write_text(current, encoding="utf-8")
        log.info("crontab backed up to %s", backup_file)

        # Append new cron job
        new_line = f"{schedule} {command} # wolfpanel:{cron_id}"

        lines = [line for line in current.splitlines() if line.strip()]
        lines.append(new_line)
        new_content = "\n".join(lines) + "\n"

        write_crontab(new_content)
        log.info("added cron job with id %s", cron_id)

        return {
            "success": True,
            "message": "Cron job created successfully",
            "cron_id": cron_id,
        }
    except Exception as exc:
        log.error("cron creation failed: %s", exc)
        return {
            "success": False,
            "error": str(exc),
        }


def handle_cron_delete(payload: dict, backup_dir: Path | None = None) -> dict:
    try:
        # Security checks on raw payload values
        for key in ["command_id", "comment"]:
            val = payload.get(key)
            if val and ("\n" in str(val) or "\r" in str(val)):
                raise ValueError(f"Newline character detected in {key}")

        cron_id = payload.get("command_id")
        if not cron_id:
            comment = payload.get("comment", "")
            if "wolfpanel-managed:" in comment:
                cron_id = comment.split("wolfpanel-managed:")[-1].strip()
            else:
                cron_id = comment.strip()

        if not cron_id:
            raise ValueError("command_id or comment containing managed id is required")

        # Get backup dir
        if backup_dir is None:
            config = load_config()
            backup_dir = config.etc_dir / "backups"

        backup_dir.mkdir(parents=True, exist_ok=True)

        # Read current crontab
        current = read_crontab()

        # Backup current
        timestamp = int(time.time())
        backup_file = backup_dir / f"crontab_{timestamp}.bak"
        backup_file.write_text(current, encoding="utf-8")
        log.info("crontab backed up to %s", backup_file)

        # Filter out cron line(s)
        lines = current.splitlines()
        new_lines = []
        skip_next = False
        found = False
        marker = f"# wolfpanel:{cron_id}"

        for line in lines:
            if skip_next:
                skip_next = False
                continue

            stripped = line.strip()
            if marker in stripped:
                found = True
                if stripped.startswith("#"):
                    skip_next = True
                continue

            new_lines.append(line)

        if not found:
            raise ValueError(f"Cron job marker '{marker}' not found in crontab")

        new_content = "\n".join(new_lines) + "\n" if new_lines else ""
        write_crontab(new_content)
        log.info("deleted cron job with id %s", cron_id)

        return {
            "success": True,
            "message": "Cron job deleted successfully",
            "cron_id": cron_id,
        }
    except Exception as exc:
        log.error("cron deletion failed: %s", exc)
        return {
            "success": False,
            "error": str(exc),
        }
