import os
from datetime import datetime
import time
import json
from remote import Remote
from variables import PATH_TO_HOST_ALERTS,PATH_TO_VM_ALERTS

class Log(Remote):
    """
    A utility class for analyzing logs from Wazuh.
    - Common to every Vmagent
    """

    def __init__(self, alert_log_file_path: str = PATH_TO_HOST_ALERTS, **kwargs):
        """
        Initialize the Log class.

        Args:
            alert_log_file_path (str): The path to the alert log file. Default is PATH_TO_HOST_ALERTS.
            **kwargs: Additional keyword arguments to pass to the superclass.
        INFO : alert_log_file_path needs to be in vagrant shared file !
        """
        super().__init__(**kwargs)
        self.log_wazuh_path = alert_log_file_path
    
    def synchronize_alerts(self):
        """
        Copy the alert.json file from the VM to the host.

        """
        
        # Create an empty dictionary
        data = {}

        # Write the empty dictionary to the JSON file
        with open(PATH_TO_HOST_ALERTS, 'w') as file:
            json.dump(data, file)
        self.synchronize_with_VM(PATH_TO_HOST_ALERTS,PATH_TO_VM_ALERTS,from_vm=True)

    def check_alerts(self, rule_id: str):
        """
        Check the alerts log for the specified rule ID.

        Args:
            rule_id (str): The rule ID to search for in the alerts log. Must be a numeric string.
        """
        # Synchronize alerts with VM
        self.synchronize_alerts()

        self.logger.debug("Running function check_alerts")
        time.sleep(5)  # Wait for the alert to be logged

        # Strip whitespace and validate the input
        rule_id = rule_id.strip()
        if not rule_id.isdigit():
            self.logger.error("Invalid rule_id: must be a numeric string.")
            raise ValueError("The rule_id must be a numeric string.")

        # Pass the validated rule_id
        return self.parse_rule_id(PATH_TO_HOST_ALERTS, rule_id)

    def extract_time(self, timestamp: str) -> str:
        """
        Extract the time part from the timestamp.

        Args:
            timestamp (str): The timestamp string.

        Returns:
            str: The extracted time in HH:MM:SS format.
        """
        # Assuming the timestamp is in the format "YYYY-MM-DDTHH:MM:SS.sssZ"
        time_part = timestamp.split('T')[1].split('.')[0]
        return time_part

    def _get_rule_id(self, rule_id: str=550) -> dict:
        """
        Get the last log entry for the current date for the provided rule ID.

        Args:
            log_file_path (str): The path to the log file.
            rule_id (str): The rule ID to search for in the log file.

        Returns:
            dict: The parsed log entry as a dictionary if found, otherwise an empty dictionary.
        """
        log_file_path = self.log_wazuh_path
        self.logger.debug(f"Running function _get_rule_id for rule ID {rule_id}")

        # Test if the log file exists and is readable
        if not os.path.isfile(log_file_path):
            self.logger.error(f"Error: Log file {log_file_path} does not exist or is not accessible.")
            return {}

        # Get today's date dynamically
        today_date = datetime.now().strftime("%Y/%m/%d")

        # Extract the last log entry with the specified rule ID and today's date
        last_log = None
        with open(log_file_path, 'r') as file:
            for line in file:
                alert = json.loads(line)
                if alert.get("rule", {}).get("id") == rule_id:
                    last_log = line

        # Check if the log entry is found
        if not last_log:
            self.logger.warning(f"Warning: No logs found for Rule ID {rule_id} on {today_date}.")
            return {}

        # Parse the JSON-like string to extract the required information
        alert = json.loads(last_log)
        return alert

    def check_fim_change(self, dir: str)-> bool:
        """
        Check if the specified directory was modified regarding rule ID 550.

        Args:
            dir (str): The directory to check.
        """
        self.logger.debug(f"Checking if directory '{dir}' was modified regarding rule ID 550.")
        time.sleep(5)  # Wait for the alert to be logged

        # NOTE: Ensure no whitespace in dir paths
        # Remove whitespace from the directory argument
        dir = dir.strip().replace(" ", "")

        #Synchronize 
        self.synchronize_alerts()

        # Get the last log entry for rule ID 550
        alert = self._get_rule_id("550")

        if alert:
            # Extract the path from the alert
            path = alert.get("syscheck", {}).get("path")

            # Check if the path matches the specified directory
            if dir in path:
                self.logger.info(f"Directory '{dir}' was modified as per rule ID 550. It occurred at file {path}.")
                return True
            else:
                self.logger.warning(f"Directory '{dir}' was not modified as per rule ID 550.")
                return False
        else:
            self.logger.warning(f"No alert found for rule ID 550.")
            return False

    def is_time_consistent(self, log_time: str, max_delay: int = 300) -> bool:
        """
        Compare the time of the log retrieval and the actual time.

        Args:
            log_time (str): The time of the log retrieval in HH:MM:SS format.
            max_delay (int): The maximum allowed delay in seconds. Default is 300 seconds (5 minutes).

        Returns:
            bool: True if the time difference is within the allowed delay, False otherwise.
        """
        self.logger.debug(f"Checking time consistency for log time {log_time} with max delay {max_delay} seconds.")

        # Get the current time in HH:MM:SS format
        current_time = datetime.now().strftime("%H:%M:%S")

        # Convert both times to seconds since the start of the day for comparison
        log_seconds = int(datetime.strptime(log_time, "%H:%M:%S").strftime("%s"))
        current_seconds = int(datetime.strptime(current_time, "%H:%M:%S").strftime("%s"))

        # Calculate the difference in seconds
        
        time_difference = current_seconds - log_seconds -3600 # BUG CAREFULL Indeed ! One hour delay on my machine

        # Check if the difference is less than the allowed delay
        if time_difference < max_delay:
            self.logger.debug(f"The log time is less than {max_delay} seconds ago: {time_difference} seconds")
            return True
        else:
            self.logger.warning(f"Warning: The log time is more than {max_delay} seconds ago: {time_difference} seconds")
            return False

    def parse_rule_id(self, log_file_path: str = None, rule_id: str=550):
        """
        Parse the log file to extract information based on the rule ID.

        Args:
            log_file_path (str): The path to the log file.
            rule_id (str): The rule ID to search for in the log file.

        Returns:
            None
        """
        log_file_path = log_file_path or self.log_wazuh_path
        self.logger.debug(f"Running function parse_rule_id for rule ID {rule_id}")

        # Get the last log entry for the rule ID
        alert = self._get_rule_id(rule_id)

        if alert:
            # Extract required fields
            active_response = alert.get("decoder", {}).get("name")
            agent_id = alert.get("Vmagent", {}).get("id")
            technique = alert.get("rule", {}).get("mitre", {}).get("technique", [None])[0]
            frequency = alert.get("rule", {}).get("firedtimes")
            log_timestamp = alert.get("timestamp")

            # Check if all required fields are extracted
            if not all([active_response, agent_id, technique, frequency]):
                self.logger.warning("Warning: Failed to extract some fields from the log entry.")
                

            # Extract time from the log timestamp
            log_time = self.extract_time(log_timestamp)

            # Log the time of last log retrieval and any delay
            self.logger.debug(f"Last log retrieved at time: {log_time}")

            # Log the extracted details
            if active_response:
                self.logger.debug(f"Active Response: {active_response}")
            else:
                self.logger.warning("Active Response: Not available")

            if agent_id:
                self.logger.debug(f"Vmagent ID: {agent_id}")
            else:
                self.logger.warning("Vmagent ID: Not available")

            if technique:
                self.logger.debug(f"Attack Technique: {technique}")
            else:
                self.logger.warning("Attack Technique: Not available")

            if frequency:
                self.logger.debug(f"Frequency: {frequency}")
            else:
                self.logger.warning("Frequency: Not available")

            # Check time consistency
            return self.is_time_consistent(log_time)
        else:
            self.logger.warning(f"No alert found for rule ID {rule_id}.")
            return False
