#!/usr/bin/env python3
"""
File Watcher Automation Script for Genesis System.

This script monitors a specified directory for file changes (creation, modification, deletion)
and triggers predefined actions based on those changes. It utilizes the `watchdog` library
for efficient file system monitoring and `logging` for comprehensive event tracking.

Example actions:
- Logging changes
- Executing external scripts
- Triggering other Genesis automation scripts
"""

import time
import os
import logging
import argparse
import json
from typing import List, Dict, Callable, Optional
import watchdog.events
import watchdog.observers

# Configure logging (adjust level and format as needed)
logging.basicConfig(
    level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
)


class FileChangeHandler(watchdog.events.FileSystemEventHandler):
    """
    Handles file system events triggered by the watchdog observer.
    """

    def __init__(
        self,
        actions: Dict[str, List[Dict[str, str]]],
        watched_directory: str,
        event_queue=None,
    ):
        """
        Initializes the FileChangeHandler.

        Args:
            actions: A dictionary mapping event types (created, modified, deleted)
                     to lists of action specifications. Each action specification is
                     a dictionary containing the "type" of action and any required
                     parameters (e.g., "script_path").
            watched_directory: The directory being watched. Used for logging context.
            event_queue: An optional queue to pass event details to other processes.
        """
        super().__init__()
        self.actions = actions
        self.watched_directory = watched_directory
        self.event_queue = event_queue
        logging.info(f"FileChangeHandler initialized for: {watched_directory}")

    def on_any_event(self, event: watchdog.events.FileSystemEvent):
        """
        Handles any file system event. This method checks if the event is relevant
        (file or directory) and dispatches to specific event handlers.

        Args:
            event: The file system event object.
        """
        if event.is_directory:
            return None

        # Ignore temporary files, OS-specific hidden files, etc.
        if event.src_path.endswith("~") or event.src_path.startswith("._"):
            return None

        if event.event_type == "created":
            self.on_created(event)
        elif event.event_type == "modified":
            self.on_modified(event)
        elif event.event_type == "deleted":
            self.on_deleted(event)
        elif event.event_type == "moved":
            self.on_moved(event)

    def on_created(self, event: watchdog.events.FileCreatedEvent):
        """
        Handles file creation events. Executes actions associated with "created" events.

        Args:
            event: The file creation event object.
        """
        self._execute_actions("created", event.src_path)
        logging.info(f"File created: {event.src_path}")
        if self.event_queue:
            self.event_queue.put(("created", event.src_path))

    def on_modified(self, event: watchdog.events.FileModifiedEvent):
        """
        Handles file modification events. Executes actions associated with "modified" events.

        Args:
            event: The file modification event object.
        """
        self._execute_actions("modified", event.src_path)
        logging.info(f"File modified: {event.src_path}")
        if self.event_queue:
            self.event_queue.put(("modified", event.src_path))

    def on_deleted(self, event: watchdog.events.FileDeletedEvent):
        """
        Handles file deletion events. Executes actions associated with "deleted" events.

        Args:
            event: The file deletion event object.
        """
        self._execute_actions("deleted", event.src_path)
        logging.info(f"File deleted: {event.src_path}")
        if self.event_queue:
            self.event_queue.put(("deleted", event.src_path))

    def on_moved(self, event: watchdog.events.FileMovedEvent):
        """
        Handles file move/rename events. Executes actions associated with "moved" events.

        Args:
            event: The file move event object.
        """
        self._execute_actions("moved", event.src_path, event.dest_path)  # Pass dest_path
        logging.info(f"File moved from {event.src_path} to {event.dest_path}")
        if self.event_queue:
            self.event_queue.put(("moved", event.src_path, event.dest_path))


    def _execute_actions(self, event_type: str, file_path: str, destination_path: Optional[str] = None):
        """
        Executes actions defined for a given event type.

        Args:
            event_type: The type of event (e.g., "created", "modified").
            file_path: The path to the file that triggered the event.
            destination_path: For "moved" events, the new file path.  Optional for other event types.
        """
        if event_type in self.actions:
            for action in self.actions[event_type]:
                try:
                    action_type = action.get("type")
                    if action_type == "log":
                        log_message = action.get("message", f"{event_type} event triggered for {file_path}")
                        logging.info(log_message)
                    elif action_type == "script":
                        script_path = action.get("script_path")
                        if script_path:
                            self._execute_script(script_path, file_path, destination_path) # Pass destination_path
                        else:
                            logging.error(f"Missing script_path for script action in {event_type} event.")
                    # Add more action types here (e.g., "trigger_genesis_script")
                    else:
                        logging.warning(f"Unknown action type: {action_type}")
                except Exception as e:
                    logging.error(f"Error executing action for {event_type} event: {e}")

    def _execute_script(self, script_path: str, file_path: str, destination_path: Optional[str] = None):
        """
        Executes an external script.

        Args:
            script_path: The path to the script to execute.
            file_path: The path to the file that triggered the event (passed as an argument to the script).
            destination_path: For move events, the destination path (passed as second argument).
        """
        try:
            import subprocess

            command = [script_path, file_path]
            if destination_path:
                command.append(destination_path)

            result = subprocess.run(
                command,
                capture_output=True,
                text=True,
                check=True,
            )
            logging.info(f"Script executed: {script_path}, Output: {result.stdout}")
            if result.stderr:
                logging.warning(f"Script executed: {script_path}, Error: {result.stderr}")

        except FileNotFoundError:
            logging.error(f"Script not found: {script_path}")
        except subprocess.CalledProcessError as e:
            logging.error(f"Script failed with exit code {e.returncode}: {e.stderr}")
        except Exception as e:
            logging.error(f"Error executing script: {script_path} - {e}")


def load_configuration(config_file: str) -> Dict:
    """
    Loads the configuration from a JSON file.

    Args:
        config_file: The path to the configuration file.

    Returns:
        A dictionary containing the configuration.
    """
    try:
        with open(config_file, "r") as f:
            config = json.load(f)
            return config
    except FileNotFoundError:
        logging.error(f"Configuration file not found: {config_file}")
        return {}
    except json.JSONDecodeError:
        logging.error(f"Invalid JSON format in: {config_file}")
        return {}
    except Exception as e:
        logging.error(f"Error loading configuration: {e}")
        return {}


def main(watched_directory: str, config_file: str, recursive: bool = True):
    """
    Main function to start the file watcher.

    Args:
        watched_directory: The directory to watch for changes.
        config_file: The path to the configuration file.
        recursive: Boolean indicating whether to watch subdirectories recursively.
    """
    config = load_configuration(config_file)
    if not config:
        logging.error("No configuration loaded, exiting.")
        return

    actions = config.get("actions", {})

    event_handler = FileChangeHandler(actions, watched_directory)
    observer = watchdog.observers.Observer()
    observer.schedule(event_handler, watched_directory, recursive=recursive)
    observer.start()

    logging.info(
        f"Watching directory: {watched_directory} (recursive={recursive}), using config: {config_file}"
    )

    try:
        while True:
            time.sleep(1)  # Adjust sleep duration as needed
    except KeyboardInterrupt:
        observer.stop()
    finally:
        observer.join()
        logging.info("File watcher stopped.")


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="File Watcher Automation Script.")
    parser.add_argument(
        "watched_directory", help="The directory to watch for file changes."
    )
    parser.add_argument(
        "--config",
        default="config.json",
        help="The path to the configuration file (default: config.json).",
    )
    parser.add_argument(
        "--no-recursive",
        action="store_false",
        dest="recursive",
        help="Disable recursive directory watching.",
    )

    args = parser.parse_args()

    # Validate directory
    if not os.path.isdir(args.watched_directory):
        print(f"Error: '{args.watched_directory}' is not a valid directory.")
        exit(1)

    # Validate config file
    if not os.path.isfile(args.config):
        print(f"Error: '{args.config}' is not a valid file.")
        exit(1)


    main(args.watched_directory, args.config, args.recursive)