#!/usr/bin/env python3

import os
import re
import sys
import time
import argparse
import subprocess
from pathlib import Path
from datetime import datetime
from urllib.parse import urlparse
from urllib.request import url2pathname

def cleanup_directory(args):
  """
  Move items in the specified directory to trash after expiration time.
  """
  current_user = subprocess.run(["id", "-n", "-u"], capture_output = True, text = True).stdout.rstrip()
  current_time = time.time()
  expiration_time = args.expiration_time
  directory = Path(args.directory).absolute()
  if subprocess.run(["which", "gio"], capture_output = True, text = True).stdout == "":
    print(datetime.fromtimestamp(current_time).strftime("%Y-%m-%d %H:%M:%S"), "`gio`: command not found.")
    sys.exit(1)
  elif not directory.exists():
    print(datetime.fromtimestamp(current_time).strftime("%Y-%m-%d %H:%M:%S"), str(directory), "does not exist.")
    sys.exit(1)
  elif not directory.is_dir():
    print(datetime.fromtimestamp(current_time).strftime("%Y-%m-%d %H:%M:%S"), str(directory), "is not a directory.")
    sys.exit(1)
  elif str(directory) == "/" or str(directory.parent) == "/":
    print(datetime.fromtimestamp(current_time).strftime("%Y-%m-%d %H:%M:%S"), str(directory), "is root directory or is directly under root directory.")
    sys.exit(1)
  else:
    for child in directory.iterdir():
      # deals with bad link and user privilege
      if not (child.exists() and child.owner() == current_user):
        continue
      if current_time - child.stat().st_atime > expiration_time and \
         current_time - child.stat().st_mtime > expiration_time and \
         current_time - child.stat().st_ctime > expiration_time:
        if child.is_file():
          print(datetime.fromtimestamp(current_time).strftime("%Y-%m-%d %H:%M:%S"),
          "Moving",
          str(child.absolute()),
          "(" + "access time:", datetime.fromtimestamp(child.stat().st_atime).strftime("%Y-%m-%d %H:%M:%S") + ",",
          "modify time:", datetime.fromtimestamp(child.stat().st_mtime).strftime("%Y-%m-%d %H:%M:%S") + ",",
          "change time:", datetime.fromtimestamp(child.stat().st_ctime).strftime("%Y-%m-%d %H:%M:%S") + ")",
          "to trash...",
          end = " ")
          # use uri instead of raw path to avoid meta characters
          result = subprocess.run(["gio", "trash", child.absolute().as_uri()], capture_output = True, text = True)
          if result.returncode == 0:
            print("Succeeded.")
          else:
            print("Failed.", "Standard Error:", result.stderr)
        elif child.is_dir():
          keep = False
          for grandchild in child.rglob("*"):
            # deals with bad link
            if not grandchild.exists():
              continue
            if not (current_time - grandchild.stat().st_atime > expiration_time and \
                    current_time - grandchild.stat().st_mtime > expiration_time and \
                    current_time - grandchild.stat().st_ctime > expiration_time and \
                    grandchild.owner() == current_user):
              keep = True
              break
          if not keep:
            print(datetime.fromtimestamp(current_time).strftime("%Y-%m-%d %H:%M:%S"),
            "Moving",
            str(child.absolute()),
            "(" + "access time:", datetime.fromtimestamp(child.stat().st_atime).strftime("%Y-%m-%d %H:%M:%S") + ",",
            "modify time:", datetime.fromtimestamp(child.stat().st_mtime).strftime("%Y-%m-%d %H:%M:%S") + ",",
            "change time:", datetime.fromtimestamp(child.stat().st_ctime).strftime("%Y-%m-%d %H:%M:%S") + ")",
            "to trash...",
            end = " ")
            # use uri instead of raw path to avoid meta characters
            result = subprocess.run(["gio", "trash", child.absolute().as_uri()], capture_output = True, text = True)
            if result.returncode == 0:
              print("Succeeded.")
            else:
              print("Failed.", "Standard Error:", result.stderr)
    return None

def cleanup_trash(args):
  """
  Delete items in trash after expiration time.
  """
  current_time = time.time()
  expiration_time = args.expiration_time
  if subprocess.run(["which", "gio"], capture_output = True, text = True).stdout == "":
    print(datetime.fromtimestamp(current_time).strftime("%Y-%m-%d %H:%M:%S"), "`gio`: command not found.")
    sys.exit(1)
  elif os.environ.get("XDG_RUNTIME_DIR") is None:
    print(datetime.fromtimestamp(current_time).strftime("%Y-%m-%d %H:%M:%S"), "Environment variable ${XDG_RUNTIME_DIR} is not set.")
    sys.exit(1)
  else:
    # use the `-u` option to get uri instead of raw path, so as to avoid meta characters
    trash_list = subprocess.run(["gio", "list", "-h", "-n", "-u", "-a", "time::changed", "trash://"], capture_output = True, text = True).stdout.rstrip("\n").split("\n")
    for trash_item in trash_list:
      trash_item_info = re.search("^(trash://\\S+)\t\\d+\t\\((regular|directory|special)\\)\ttime::changed=(\\d+)$", trash_item)
      if trash_item != "" and trash_item_info is None:
        print(datetime.fromtimestamp(current_time).strftime("%Y-%m-%d %H:%M:%S"), "Cannot identify any trash item because regex pattern is not matched.")
        break
      else:
        trash_item_uri, _, trash_item_ctime = trash_item_info.groups()
        trash_item_ctime = float(trash_item_ctime)
        if current_time - trash_item_ctime > expiration_time:
          print(datetime.fromtimestamp(current_time).strftime("%Y-%m-%d %H:%M:%S"),
          "Deleting",
          url2pathname(urlparse(trash_item_uri).path),
          "(" + "change time:", datetime.fromtimestamp(trash_item_ctime).strftime("%Y-%m-%d %H:%M:%S") + ")",
          "from trash...",
          end = " ")
          result = subprocess.run(["gio", "remove", trash_item_uri], capture_output = True, text = True)
          if result.returncode == 0:
            print("Succeeded.")
          else:
            print("Failed.", "Standard Error:", result.stderr)
    return None

if __name__ == "__main__":
  parser = argparse.ArgumentParser()
  subparsers = parser.add_subparsers(dest = "subcommand", required = True)
  parser.add_argument("-t", "--expiration-time", dest = "expiration_time", type = int, required = True, help = "Amount of time (in seconds) after which the item(s) will be moved to trash or deleted from trash.")
  # argument parser for `cleanup directory`
  parser_cleanup_directory = subparsers.add_parser("directory")
  parser_cleanup_directory.set_defaults(func = cleanup_directory)
  parser_cleanup_directory.add_argument("-d", "--directory", dest = "directory", type = str, required = True, help = "Path of the directory whose items will be cleaned after expiration time.")
  # argument parser for `cleanup trash`
  parser_cleanup_trash = subparsers.add_parser("trash")
  parser_cleanup_trash.set_defaults(func = cleanup_trash)
  # execute command
  args = parser.parse_args()
  args.func(args)
