'''
  __  __           _            
 |  \/  | __ _ ___| |_ ___ _ __ 
 | |\/| |/ _` / __| __/ _ \ '__|
 | |  | | (_| \__ \ ||  __/ |   
 |_|  |_|\__,_|___/\__\___|_|   
                                
Master Python script that will trigger other scripts at every chosen time pace
This script is triggered as a systemd service used as an alternative to cronjobs

Attention: 
    to do -> prevent SARA R4 Script to run again if it's taking more than 60 secs to finish (using a lock file ??) 

First time: need to create the service file

--> sudo nano /etc/systemd/system/master_nebuleair.service

⬇️
[Unit]
Description=Master manager for the Python loop scripts
After=network.target

[Service]
ExecStart=/usr/bin/python3 /var/www/nebuleair_pro_4g/master.py
Restart=always
User=root
WorkingDirectory=/var/www/nebuleair_pro_4g
StandardOutput=append:/var/www/nebuleair_pro_4g/logs/master.log
StandardError=append:/var/www/nebuleair_pro_4g/logs/master_errors.log

[Install]
WantedBy=multi-user.target
⬆️

Reload systemd (first time after creating the service):
    sudo systemctl daemon-reload

Enable (once), start (once and after stopping) and restart (after modification)systemd:
    sudo systemctl enable master_nebuleair.service
    sudo systemctl start master_nebuleair.service
    sudo systemctl restart master_nebuleair.service

Check the service status:
    sudo systemctl status master_nebuleair.service


Specific scripts can be disabled with config.json
    Exemple: stop gathering data from NPM
    Exemple: stop sending data with SARA R4

'''
import time
import threading
import subprocess
import os
import sqlite3


# Base directory where scripts are stored
SCRIPT_DIR = "/var/www/nebuleair_pro_4g/"
DB_PATH = "/var/www/nebuleair_pro_4g/sqlite/sensors.db"

# Lock file path for SARA script
SARA_LOCK_FILE = "/var/www/nebuleair_pro_4g/sara_script.lock"


def is_script_enabled(script_name):
    """Check if a script is enabled in the database."""
    try:
        conn = sqlite3.connect(DB_PATH)
        cursor = conn.cursor()
        
        cursor.execute(
            "SELECT enabled FROM config_scripts_table WHERE script_path = ?",
            (script_name,)
        )
        
        result = cursor.fetchone()
        conn.close()
        
        if result is None:
            return True  # Default to enabled if not found in database
        
        return bool(result[0])
    except Exception:
        # If any database error occurs, default to enabled
        return True


def create_lock_file():
    """Create a lock file for the SARA script."""
    with open(SARA_LOCK_FILE, 'w') as f:
        f.write(str(int(time.time())))


def remove_lock_file():
    """Remove the SARA script lock file."""
    if os.path.exists(SARA_LOCK_FILE):
        os.remove(SARA_LOCK_FILE)


def is_script_locked():
    """Check if the SARA script is currently locked."""
    if not os.path.exists(SARA_LOCK_FILE):
        return False
    
    # Check if lock is older than 60 seconds (stale)
    with open(SARA_LOCK_FILE, 'r') as f:
        try:
            lock_time = int(f.read().strip())
            if time.time() - lock_time > 60:
                # Lock is stale, remove it
                remove_lock_file()
                return False
        except ValueError:
            # Invalid lock file content
            remove_lock_file()
            return False
    
    return True


def run_script(script_name, interval, delay=0):
    """Run a script in a synchronized loop with an optional start delay."""
    script_path = os.path.join(SCRIPT_DIR, script_name)  # Build full path
    next_run = time.monotonic() + delay  # Apply the initial delay

    while True:
        if is_script_enabled(script_name):
            # Special handling for SARA script to prevent concurrent runs
            if script_name == "loop/SARA_send_data_v2.py":
                if not is_script_locked():
                    create_lock_file()
                    try:
                        subprocess.run(["python3", script_path], timeout=200)
                    finally:
                        remove_lock_file()
            else:
                # Run other scripts normally
                subprocess.run(["python3", script_path])
        
        # Wait until the next exact interval
        next_run += interval
        sleep_time = max(0, next_run - time.monotonic())  # Prevent negative sleep times
        time.sleep(sleep_time)


# Define scripts and their execution intervals (seconds)
SCRIPTS = [
    # Format: (script_name, interval_in_seconds, start_delay_in_seconds)
    ("NPM/get_data_modbus_v3.py", 10, 0),       # Get NPM data (modbus 5 channels) every 10s
    ("envea/read_value_v2.py", 10, 0),          # Get Envea data every 10s
    ("loop/SARA_send_data_v2.py", 60, 1),       # Send data every 60 seconds, with 1s delay
    ("BME280/get_data_v2.py", 120, 0),          # Get BME280 data every 120 seconds
    ("MPPT/read.py", 120, 0),                   # Get MPPT data every 120 seconds
    ("sqlite/flush_old_data.py", 86400, 0)      # Flush old data inside db every day
]

# Start threads for scripts
for script_name, interval, delay in SCRIPTS:
    thread = threading.Thread(target=run_script, args=(script_name, interval, delay), daemon=True)
    thread.start()

# Keep the main script running
while True:
    time.sleep(1)