#!/usr/bin/php
<?php

require_once('/usr/share/ombutel/www/includes/cli.php');

use ombutel\db;
use ombutel\external_resource;
use ombutel\timezone;

if (is_null($cfg = parse_args())) {
	echo "Usage: {$argv[0]} --cos NAME --[hours/days/weeks/months/years] N [-d] [-D] [-t] [-T]\n".
		"Delete CDR records older than specified time period, filtered by Class of Service.\n".
		"  --cos       Class of Service name (e.g. 1, sales). Required.\n".
		"  --hours     Delete records older than N hours.\n".
		"  --days      Delete records older than N days.\n".
		"  --weeks     Delete records older than N weeks.\n".
		"  --months    Delete records older than N months.\n".
		"  --years     Delete records older than N years.\n".
		"  -d          Also delete Local audio recordings.\n".
		"  -D          Also delete External Storage audio recordings.\n".
		"  -t          Also delete Local transcription files.\n".
		"  -T          Also delete External Storage transcriptions.\n".
		"\n";
} else {
	prune($cfg);
}

function parse_args() {
	global $argc, $argv;

	$unit = null;
	$amount = null;
	$cos = null;
	$delete_recordings = null;
	$delete_external_recordings = null;
	$delete_transcriptions = null;
	$delete_external_transcriptions = null;

	for ($i = 1; $i < $argc; ++$i) {
		if (is_null($cos) && $argv[$i] == '--cos') {
			if (++$i < $argc && preg_match('/^[a-z0-9\-_]+$/i', $argv[$i])) {
				$cos = $argv[$i];
			} else {
				return null;
			}
		} else if (is_null($unit) && preg_match('/^--(hours|days|weeks|months|years)$/', $argv[$i])) {
			$unit = substr($argv[$i], 2);
			if (++$i < $argc && preg_match('/^\d+$/', $argv[$i])) {
				$amount = intval($argv[$i]);
			} else {
				return null;
			}
		} else if (is_null($delete_recordings) && $argv[$i] == '-d') {
			$delete_recordings = true;
		} else if (is_null($delete_external_recordings) && $argv[$i] == '-D') {
			$delete_external_recordings = true;
		} else if (is_null($delete_transcriptions) && $argv[$i] == '-t') {
			$delete_transcriptions = true;
		} else if (is_null($delete_external_transcriptions) && $argv[$i] == '-T') {
			$delete_external_transcriptions = true;
		} else {
			return null;
		}
	}

	if (!is_null($unit) && !is_null($amount) && !is_null($cos)) {
		return (object) [
			'unit'                         => $unit,
			'amount'                       => $amount,
			'cos'                          => $cos,
			'delete_recordings'            => !is_null($delete_recordings),
			'delete_external_recordings'   => !is_null($delete_external_recordings),
			'delete_transcriptions'        => !is_null($delete_transcriptions),
			'delete_external_transcriptions' => !is_null($delete_external_transcriptions),
		];
	}
}

function prune($cfg) {
	if ($cfg->amount > 0) {
		switch ($cfg->unit) {
			case 'hours':  return prune_interval('HOUR',  $cfg);
			case 'days':   return prune_interval('DAY',   $cfg);
			case 'weeks':  return prune_interval('WEEK',  $cfg);
			case 'months': return prune_interval('MONTH', $cfg);
			case 'years':  return prune_interval('YEAR',  $cfg);
		}
	}
}

/*
 * Returns a subquery that selects cdr_id values matching the given CoS and age.
 *
 * Join logic mirrors test-rec-settings in sub-call-recording:
 *   calltype 1 (internal) / 2 (incoming)  -> destination extension (dst)
 *   calltype 3 (outgoing) / 4 (transit)   -> source extension (src)
 *
 * Records where the extension no longer exists are not matched and are
 * left for the regular prune_cdr task to handle.
 */
function cos_subquery($unit, $amount, $cos) {
	return "select c.`cdr_id`
		from `asterisk`.`cdr` c
		join `ombutel`.`ombu_extensions` e
			on (c.`calltype` in (1,2) and e.`extension` = c.`dst`)
			or (c.`calltype` in (3,4) and e.`extension` = c.`src`)
		join `ombutel`.`ombu_classes_of_service` cos
			on e.`class_of_service_id` = cos.`class_of_service_id`
		where c.`calldate` < date_sub(now(), interval {$amount} {$unit})
		and cos.`cos` = '{$cos}'";
}

function prune_interval($unit, $cfg) {
	db::begin_transaction();
	// Wrap in a derived table so MySQL allows deleting from asterisk.cdr
	// while the inner subquery also references it.
	$subq  = cos_subquery($unit, $cfg->amount, $cfg->cos);
	$range = "`cdr_id` in (select `cdr_id` from ({$subq}) as _cos_ids)";

	if ($cfg->delete_recordings) {
		delete_local_recordings($range);
	}
	if ($cfg->delete_external_recordings) {
		delete_external_storage_recordings($range);
	}
	if ($cfg->delete_transcriptions) {
		delete_local_transcriptions($range);
	}
	if ($cfg->delete_external_transcriptions) {
		delete_external_storage_transcriptions($range);
	}
	if ($cfg->delete_transcriptions || $cfg->delete_external_transcriptions) {
		db::query("delete t from `asterisk`.`tcdr` t
			join `asterisk`.`cdr` c on c.`uniqueid` = t.`uniqueid`
			where c.{$range}");
	}
	db::query("delete from `asterisk`.`cdr` where {$range}");
	db::commit();
}

function delete_local_recordings($range) {
	$result = db::query(
		"select `recfile` from `asterisk`.`cdr`
		where {$range} and `recfile` like '/%'");
	foreach ($result as $row) {
		@unlink($row->recfile);
	}
}

function delete_external_storage_recordings($range) {
	$result = db::query(
		"select `recfile_cloud` from `asterisk`.`cdr`
		where {$range} and `recfile_cloud` is not null");
	foreach ($result as $row) {
		$timezone = new DateTimeZone(timezone::current()->identifier);
		$datetime_now = (new DateTime("now", $timezone))->format("Y-m-d H:i:s");
		if (count($parts = explode(",", $row->recfile_cloud, 2)) > 1) {
			list($resource_id, $object_name) = $parts;
			if ($resource = external_resource::get($resource_id)) {
				try {
					@$resource->delete_file($object_name);
				} catch (Exception $e) {
					file_put_contents("/var/log/cpbx/cdr_cleanup.log", "{$datetime_now} E: Failed deleting \"{$object_name}\" from External Storage Provider \"{$resource->name}\"\n", FILE_APPEND);
				}
			} else {
				file_put_contents("/var/log/cpbx/cdr_cleanup.log", "{$datetime_now} E: The External Storage Provider for the CDR Record referring \"{$object_name}\" no longer exists\n", FILE_APPEND);
			}
		}
	}
}

function delete_local_transcriptions($range) {
	$result = db::query(
		"select t.`transfile` from `asterisk`.`cdr` c
		join `asterisk`.`tcdr` t on c.`uniqueid` = t.`uniqueid`
		where c.{$range} and t.`transfile` != ''");
	foreach ($result as $row) {
		@unlink($row->transfile);
	}
}

function delete_external_storage_transcriptions($range) {
	$result = db::query(
		"select t.`transfile_cloud` from `asterisk`.`cdr` c
		join `asterisk`.`tcdr` t on c.`uniqueid` = t.`uniqueid`
		where c.{$range} and t.`transfile_cloud` != ''");
	foreach ($result as $row) {
		$timezone = new DateTimeZone(timezone::current()->identifier);
		$datetime_now = (new DateTime("now", $timezone))->format("Y-m-d H:i:s");
		if (count($parts = explode(",", $row->transfile_cloud, 2)) > 1) {
			list($resource_id, $object_name) = $parts;
			if ($resource = external_resource::get($resource_id)) {
				try {
					@$resource->delete_file($object_name);
				} catch (Exception $e) {
					file_put_contents("/var/log/cpbx/cdr_cleanup.log", "{$datetime_now} E: Failed deleting transcription \"{$object_name}\" from External Storage Provider \"{$resource->name}\"\n", FILE_APPEND);
				}
			} else {
				file_put_contents("/var/log/cpbx/cdr_cleanup.log", "{$datetime_now} E: The External Storage Provider for the transcription \"{$object_name}\" no longer exists\n", FILE_APPEND);
			}
		}
	}
}
?>
