#!/usr/bin/env python3

"""
cron_monitor.py - A wrapper for cron jobs that tracks execution, captures output, and alerts on failures.

This script wraps a command, executes it, records execution time, captures stdout/stderr,
and can alert on failures via webhook. All execution history is stored in SQLite.
"""

import argparse
import logging
import os
import sqlite3
import subprocess
import sys
import time
from typing import Dict, List, Optional, Tuple

import httpx

# Constants
DB_PATH = os.path.expanduser("~/.cron_monitor.db")
DEFAULT_WEBHOOK_URL = ""
LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s"

# Configure logging
logging.basicConfig(format=LOG_FORMAT, level=logging.INFO)
logger = logging.getLogger(__name__)


class CronMonitor:
    """
    A class to monitor cron job executions.
    """

    def __init__(self, db_path: str = DB_PATH, webhook_url: Optional[str] = None):
        """
        Initialize the CronMonitor.

        Args:
            db_path: Path to the SQLite database
            webhook_url: URL for webhook notifications on failure
        """
        self.db_path = db_path
        self.webhook_url = webhook_url
        self._init_db()

    def _init_db(self) -> None:
        """
        Initialize the SQLite database with the required table.
        """
        try:
            with sqlite3.connect(self.db_path) as conn:
                cursor = conn.cursor()
                cursor.execute(
                    """
                    CREATE TABLE IF NOT EXISTS cron_executions (
                        id INTEGER PRIMARY KEY AUTOINCREMENT,
                        timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
                        command TEXT NOT NULL,
                        exit_code INTEGER,
                        execution_time REAL,
                        stdout TEXT,
                        stderr TEXT
                    )
                    """
                )
                conn.commit()
        except sqlite3.Error as e:
            logger.error(f"Database initialization failed: {e}")
            raise

    def record_execution(
        self,
        command: str,
        exit_code: int,
        execution_time: float,
        stdout: str,
        stderr: str,
    ) -> None:
        """
        Record a command execution in the database.

        Args:
            command: The executed command
            exit_code: The exit code of the command
            execution_time: Execution time in seconds
            stdout: Standard output of the command
            stderr: Standard error of the command
        """
        try:
            with sqlite3.connect(self.db_path) as conn:
                cursor = conn.cursor()
                cursor.execute(
                    """
                    INSERT INTO cron_executions 
                    (command, exit_code, execution_time, stdout, stderr)
                    VALUES (?, ?, ?, ?, ?)
                    """,
                    (command, exit_code, execution_time, stdout, stderr),
                )
                conn.commit()
        except sqlite3.Error as e:
            logger.error(f"Failed to record execution: {e}")

    def send_alert(self, command: str, exit_code: int, stderr: str) -> None:
        """
        Send an alert notification via webhook if configured.

        Args:
            command: The command that failed
            exit_code: The exit code of the command
            stderr: Standard error of the command
        """
        if not self.webhook_url:
            return

        try:
            payload = {
                "command": command,
                "exit_code": exit_code,
                "stderr": stderr,
                "timestamp": time.strftime("%Y-%m-%dT%H:%M:%S"),
            }
            with httpx.Client() as client:
                response = client.post(self.webhook_url, json=payload, timeout=10)
                response.raise_for_status()
            logger.info(f"Alert sent successfully for {command}")
        except httpx.HTTPError as e:
            logger.error(f"Failed to send alert: {e}")
        except Exception as e:
            logger.error(f"An unexpected error occurred while sending alert: {e}")
def main():
    parser = argparse.ArgumentParser(description="Cron job monitor")
    parser.add_argument("command", help="The command to execute")
    parser.add_argument("--webhook_url", help="Webhook URL for alerts")
    args = parser.parse_args()

    monitor = CronMonitor(webhook_url=args.webhook_url)

    start_time = time.time()
    process = subprocess.Popen(args.command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    stdout, stderr = process.communicate()
    end_time = time.time()
    execution_time = end_time - start_time
    exit_code = process.returncode

    monitor.record_execution(args.command, exit_code, execution_time, stdout.decode(), stderr.decode())

    if exit_code != 0:
        monitor.send_alert(args.command, exit_code, stderr.decode())
        sys.exit(1)

if __name__ == "__main__":
    main()