import datetime
import os
import time

import dateutil.parser

from weevely.core import messages
from weevely.core.loggers import log
from weevely.core.module import Module
from weevely.core.vectors import ModuleExec
from weevely.core.vectors import Os
from weevely.core.vectors import PhpCode
from weevely.core.vectors import ShellCmd


class Touch(Module):
    """Change file timestamp."""

    aliases = ["touch"]

    def init(self):
        self.register_info({"author": ["Emilio Pinna"], "license": "GPLv3"})

        self.register_vectors(
            [
                PhpCode("touch('${rpath}', ${epoch_ts});", name="php_touch"),
                ShellCmd("touch -d @${epoch_ts} '${rpath}'", name="sh_touch", target=Os.NIX),
            ]
        )

        self.register_arguments(
            [
                {"name": "rpath", "help": "Remote file path"},
                {"name": "-epoch-ts", "help": "Epoch timestamp", "type": int},
                {"name": "-human-ts", "help": "Human readable timestamp e.g. '2004-02-29 16:21:42' or '16:21'"},
                {"name": "-file-ts", "help": "Clone timestamp from another file"},
                {
                    "name": "-oldest-file-ts",
                    "help": "Clone timestamp from the oldest file in the same folder",
                    "action": "store_true",
                    "default": False,
                },
                {"name": "-vector", "choices": self.vectors.get_names(), "default": "php_touch"},
            ]
        )

    def run(self, **kwargs):
        # Handle the cloning of the oldest timestamp in folder
        if self.args.get("oldest_file_ts"):
            # TODO: This works only in remote unix environment, fix it.
            folder = os.path.split(self.args["rpath"])[0] if os.path.sep in self.args["rpath"] else "."

            file_list = [os.path.join(folder, f) for f in ModuleExec("file_ls", [folder]).run()]

            for file in file_list:
                file_time = ModuleExec("file_check", [file, "time"]).run()
                self.args["epoch_ts"] = (
                    file_time if (not self.args.get("epoch_ts") or file_time < self.args.get("epoch_ts")) else None
                )

        # Handle to get timestamp from another file
        elif self.args.get("file_ts"):
            self.args["epoch_ts"] = ModuleExec("file_check", [self.args["file_ts"], "time"]).run()

        # Handle to get an human readable timestamp
        elif self.args.get("human_ts"):
            try:
                self.args["epoch_ts"] = int(
                    time.mktime(dateutil.parser.parse(self.args["human_ts"], yearfirst=True).timetuple())
                )
            except:
                log.warn(messages.module_file_touch.error_invalid_timestamp_format)
                return None

        if not self.args.get("epoch_ts"):
            log.warn(messages.module_file_touch.error_source_timestamp_required)
            return None

        self.vectors.get_result(self.args["vector"], self.args)

        # Verify execution
        if not self.args["epoch_ts"] == ModuleExec("file_check", [self.args.get("rpath"), "time"]).run():
            log.warn(messages.module_file_touch.failed_touch_file)
            return None

        return self.args["epoch_ts"]

    def print_result(self, result):
        """Override print_result to print timestamp in an human readable form"""
        if result:
            log.info("New timestamp: %s" % datetime.datetime.fromtimestamp(result).strftime("%Y-%m-%d %H:%M:%S"))
