<?php

$cli_commands=array();
function add_cli_command($command, $handler, $short_desc, $usage_args=false, $long_desc=false,
                         $override=false) {
  global $cli_commands;
  if (array_key_exists($command, $cli_commands) && !$override) {
    logging('ERROR', _("The CLI command '%s' already exists.", $command));
    return False;
  }

  if (!is_callable($handler)) {
    logging('ERROR', _("The CLI command '%s' handler is not callable !"), $command);
    return False;
  }

  $cli_commands[$command] = array (
    'handler' => $handler,
    'short_desc' => $short_desc,
    'usage_args' => $usage_args,
    'long_desc' => $long_desc,
  );
  return True;
}

/*
 *************************************************************************************************
 *                /!\ Code after this message will only be execute on CLI context /!\
 *************************************************************************************************
 */
if (php_sapi_name() != "cli")
  return true;

// Store current CLI command
$cli_command = null;

/**
 * CLI Helpers
 **/

function usage($error=false) {
  global $cli_commands, $cli_command, $argv;

  // If more than 1 arguments passed, format error message using sprintf
  if (func_num_args() > 1) {
    $error = call_user_func_array(
      'sprintf',
      array_merge(array($error), array_slice(func_get_args(), 1))
    );
  }

  if ($error)
    echo "$error\n\n";
  printf(_("Usage: %s [-h] [-qd] command\n"), basename($argv[0]));
  echo _("  -h        Show this message\n");
  echo _("  -q / -d   Quiet/Debug mode\n");
  echo _("  --trace   Trace mode (the most verbose)\n");
  echo _("  command   Command to run\n");
  echo "\n";
  echo _("Available commands:\n");

  foreach ($cli_commands as $command => $info) {
    if ($cli_command && $command != $cli_command)
      continue;
    echo (
      "  ".str_replace(
        "\n", "\n    ",
        wordwrap("$command : "._($info['short_desc'])))
      ."\n\n");
    echo (
      "    ".basename($argv[0])." $command ".
      ($info['usage_args']?_($info['usage_args']):'').
      "\n");
    if ($info['long_desc']) {
      if (is_array($info['long_desc'])) {
        $lines = array();
        foreach ($info['long_desc'] as $line)
          $lines[] = _($line);
        $info['long_desc'] = implode("\n", $lines);
      }
      else
        $info['long_desc'] = _($info['long_desc']);

      echo "\n    ".str_replace("\n", "\n    ", wordwrap($info['long_desc']))."\n";
    }
    echo "\n";
  }

  exit(($error?1:0));
}

function handle_cli_args() {
  global $log_level, $cli_commands, $cli_command, $argv;
  $log_level = 'INFO';
  $cli_command = false;
  $command_args = array();
  for ($i=1; $i < count($argv); $i++) {
    if (array_key_exists($argv[$i], $cli_commands)) {
      if (!$cli_command)
        $cli_command = $argv[$i];
      else
        usage(_("Only one command could be executed !"));
    }
    else {
      switch($argv[$i]) {
        case '-h':
        case '--help':
          usage();
          break;
        case '-d':
        case '--debug':
          $log_level = 'DEBUG';
          break;
        case '-q':
        case '--quiet':
          $log_level = 'WARNING';
          break;
        case '--trace':
          $log_level = 'TRACE';
          break;
        default:
          if ($cli_command)
            $command_args[] = $argv[$i];
          else
            usage(
              _(
                "Invalid parameter \"%s\".\nNote: Command's parameter/argument must be place ".
                "after the command."
              ), $argv[$i]
            );
      }
    }
  }

  if (!$cli_command)
    usage();

  logging(
    'DEBUG', "Run %s command %s with argument(s) '%s'.",
    basename($argv[0]), $cli_command, implode("', '", $command_args)
  );

  try {
    $result = call_user_func($cli_commands[$cli_command]['handler'], $command_args);

    exit($result?0:1);
  }
  catch(Exception $e) {
    log_exception($e, _("An exception occured running command %s"), $cli_command);
    exit(1);
  }
}

function print_item_info($item) {
  printf(_("Item #%s:\n"), $item['id']);
  printf("\t"._("ID: %s")."\n", $item['id']);
  printf("\t"._("Name: '%s'")."\n", $item['name']);
  printf("\t"._("Date: %s")."\n", format_time($item['date']));
  printf(
    "\t"._("Description: %s")."\n",
    ($item['description']?"'".$item['description']."'":_("Not set"))
  );
  printf("\t"._("Status: %s")."\n", $item['status']);
  return true;
}


/**
 * Common CLI commands
 **/

$orderbys = array('id', 'name', 'date', 'status', 'description');
function cli_list($command_args) {
  global $orderbys;
  $params = array(
    'order' => $orderbys[0],
    'order_direction' => 'ASC',
    'all' => true,
  );
  $patterns = array();
  for($i=0; $i < count($command_args); $i++) {
    switch($command_args[$i]) {
      case '-o':
      case '--orderby':
        $i++;
        if(!in_array($command_args[$i], $orderbys))
          usage('Invalid --orderby clause');
        $params['order'] = $command_args[$i];
        break;
      case '-r':
      case '--reverse':
        $params['order_direction'] = 'DESC';
        break;
      case '-s':
      case '--status':
        $i++;
        if(!check_status($command_args[$i]))
          usage('Invalid -s/--status clause');
        $params['status'] = $command_args[$i];
        break;
      default:
        $patterns[] = $command_args[$i];
    }
  }

  if (!empty($patterns))
    $params['pattern'] = implode(' ', $patterns);

  $items = search_items($params);
  if (!is_array($items)) {
    logging("ERROR", "Invalid DB info return.\n");
    return False;
  }

  if ($items['count'] == 0){
    echo _("No item.\n");
    return True;
  }

  $tbl = new Console_Table();
  $tbl->setHeaders(
    array(
      'ID',
      'Name',
      'Date',
      'Status',
      'Description',
    )
  );
  foreach($items['items'] as $info) {
    $tbl->addRow(
      array(
        $info['id'],
        $info['name'],
        format_time($info['date']),
        $info['status'],
        ($info['description']?$info['description']:''),
      )
    );
  }
  echo $tbl->getTable();
  echo "\n".sprintf(_("%d item(s)"), $items['count'])."\n";
  return True;
}
add_cli_command(
  'list',
  'cli_list',
  ___("List/search items"),
  ___("[patterns]"),
  array(
    ___("-o|--orderby       Ordering list criterion. Possible values:"),
    "                     - ".implode("\n                     - ", $orderbys),
    ___("-r|--reverse       Reverse order"),
    ___("-s|--status        Filter on status. Possible values:"),
    "                     - ".implode("\n                     - ", array_keys($status_list)),
  )
);

function cli_show($command_args) {
  if (count($command_args) != 1 || !check_id($command_args[0]))
    usage(_('You must provide a valid ID.'));

  $item_id = $command_args[0];
  $item = get_item($item_id);

  if (!$item)
    logging('FATAL', _("Item #%s not found."), $item_id);

  print_item_info($item);
  return True;
}
add_cli_command(
  'show',
  'cli_show',
  ___("Show item"),
  ___("[ID]")
);

function cli_delete($command_args) {
  if (count($command_args) != 1)
    usage(_('You must provide item ID.'));

  // Check URI
  if (!check_id($command_args[0]))
    logging('FATAL', _("Invalid item ID"));

  // Check exist
  $item_id = $command_args[0];
  $item = get_item($item_id);
  if (!$item)
    logging('FATAL', _("Item #%s not found."), $item_id);

  print_item_info($item);

  // Sure ?
  echo _("Are you sure you want to delete this item?  Type 'yes' to continue: ");
  $handle = fopen ("php://stdin","r");
  $line = fgets($handle);
  if(trim($line) != 'yes'){
    logging('WARNING', _("User cancel"));
    exit;
  }
  echo "\n";

  if (!delete_item($item['id']))
    logging('FATAL', _("An error occured deleting item #%d."), $item_id);

  return True;
}
add_cli_command(
  'delete',
  'cli_delete',
  ___("Delete item"),
  ___("[item ID]")
);

function cli_export($command_args) {
  $fd = fopen((count($command_args) >= 1?$command_args[0]:'php://output'), 'w');
  export_items($fd);
  fclose($fd);
  logging('INFO', "Items export to '".(count($command_args) >= 1?$command_args[0]:'STDOUT')."'.");
}
add_cli_command(
  'export',
  'cli_export',
  ___("Export items (as CSV)"),
  ___("[output file path]")
);

function cli_restore($command_args) {
  $fd = fopen((count($command_args) >= 1?$command_args[0]:'php://stdin'), 'r');
  restore_items($fd);
  fclose($fd);
  logging('INFO', sprint(
    "Items restored from '%s'",
    (count($command_args) >= 1?$command_args[0]:'STDIN')
  ));
}
add_cli_command(
  'restore',
  'cli_restore',
  ___("Restore items (from CSV)"),
  ___("[input file path]")
);

function cli_cron($command_args) {
  global $item_max_age;
  if (!isset($item_max_age))
    $item_max_age = 30;

  $just_try = false;
  for($i=0; $i < count($command_args); $i++) {
    switch($command_args[$i]) {
      case '-m':
      case '--max-age':
        $i++;
        if(!check_id($command_args[$i]))
          usage('Invalid -m|--max-age clause');
        $item_max_age = $command_args[$i];
        break;
      case '-j':
      case '--just-try':
        $just_try = true;
        break;
      default:
        usage('Invalid parameter '.$command_args[$i]);
    }
  }

  if (!is_int($item_max_age) || $item_max_age <= 0)
    logging(
      'FATAL',
      'Invalid $item_max_age value set in configuration: it\'s must be a positive integer.');
  logging('DEBUG', "cli_cron(): item max age = $item_max_age day(s)");

  $limit = time() - ($item_max_age * 86400);
  logging('DEBUG', "Handle items expiration with creation date limit ".format_time($limit).".");

  $items = search_items(array('all' => true));
  $error = false;
  foreach($items['items'] as $item) {
    if ($item['date'] < $limit) {
      if ($just_try) {
        logging('DEBUG', 'Just-try mode: do not really delete item #%s (%s, creation date: %s)',
          $item['id'], $item['name'], format_time($item['date'])
        );
      }
      else if (delete_item($item['id'])) {
        logging('INFO', 'Item #%s (%s) deleted (creation date: %s)',
          $item['id'], $item['name'], format_time($item['date'])
        );
        remove_item_attachments($item['id']);
      }
      else {
        logging('ERROR', 'Fail to delete item "%s" (%s, creation date: %s)',
          $item['id'], $item['name'], format_time($item['date'])
        );
        $error = true;
      }
    }
    else {
      logging('DEBUG', 'Item "%s" (%s) still valid (creation date: %s)',
        $item['id'], $item['name'], format_time($item['date'])
      );
    }
  }
  exit($error?1:0);
}
add_cli_command(
  'cron',
  'cli_cron',
  ___("Cron to handle item expiration"),
  false,
  array (
    ___("-j/--just-try    Just-try mode : do not really removed expired item(s)"),
    ___("-m/--max-age     Item expiration limit (in days, optional)"),
  )
);
