<?php
// =============================================================================
// retention_cleanup.php - 로그/측정 보관기간 정리 (SMS 로그, 센서 로그, 측정 이력,
//                          관리자 감사로그)
//
// Usage:
//   php retention_cleanup.php --dry-run
//   php retention_cleanup.php --execute
// =============================================================================

if (php_sapi_name() !== 'cli') {
    http_response_code(403);
    header('Content-Type: text/plain; charset=utf-8');
    echo "Forbidden\n";
    exit;
}

require_once __DIR__ . '/config.php';
require_once __DIR__ . '/admin_security.php';

$execute = in_array('--execute', $argv, true);
$dryRun = !$execute || in_array('--dry-run', $argv, true);

function retention_cutoff(int $days): string {
    return date('Y-m-d H:i:s', time() - ($days * 86400));
}

function retention_count(PDO $db, string $sql, array $params): int {
    $stmt = $db->prepare($sql);
    $stmt->execute($params);
    return (int)$stmt->fetchColumn();
}

function retention_delete(PDO $db, string $sql, array $params, bool $dryRun): int {
    if ($dryRun) {
        return 0;
    }
    $stmt = $db->prepare($sql);
    $stmt->execute($params);
    return $stmt->rowCount();
}

$summary = [
    'mode' => $dryRun ? 'dry-run' : 'execute',
    'sms_log_retention_days' => SMS_LOG_RETENTION_DAYS,
    'sensor_log_retention_days' => SENSOR_LOG_RETENTION_DAYS,
    'sensor_metric_retention_days' => SENSOR_METRIC_RETENTION_DAYS,
    'admin_audit_retention_days' => ADMIN_AUDIT_RETENTION_DAYS,
    'sms_log' => ['matched' => 0, 'rows_deleted' => 0],
    'sensor_log' => ['matched' => 0, 'rows_deleted' => 0],
    'sensor_metric' => ['matched' => 0, 'rows_deleted' => 0],
    'admin_audit' => ['kept_lines' => null, 'removed_lines' => null],
];

try {
    $db = get_db();
} catch (Throwable $e) {
    $summary['error'] = 'DB connection failed: ' . $e->getMessage();
    header('Content-Type: application/json; charset=utf-8');
    echo json_encode($summary, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) . PHP_EOL;
    exit(2);
}

$smsCutoff = retention_cutoff(SMS_LOG_RETENTION_DAYS);
$summary['sms_log']['matched'] = retention_count($db, "SELECT COUNT(*) FROM sms_log WHERE sent_at < :cutoff", [':cutoff' => $smsCutoff]);
$summary['sms_log']['rows_deleted'] = retention_delete($db, "DELETE FROM sms_log WHERE sent_at < :cutoff", [':cutoff' => $smsCutoff], $dryRun);

$sensorCutoff = retention_cutoff(SENSOR_LOG_RETENTION_DAYS);
$summary['sensor_log']['matched'] = retention_count($db, "SELECT COUNT(*) FROM sensor_log WHERE created_at < :cutoff", [':cutoff' => $sensorCutoff]);
$summary['sensor_log']['rows_deleted'] = retention_delete($db, "DELETE FROM sensor_log WHERE created_at < :cutoff", [':cutoff' => $sensorCutoff], $dryRun);

$metricCutoff = retention_cutoff(SENSOR_METRIC_RETENTION_DAYS);
$summary['sensor_metric']['matched'] = retention_count($db, "SELECT COUNT(*) FROM sensor_metric WHERE created_at < :cutoff", [':cutoff' => $metricCutoff]);
$summary['sensor_metric']['rows_deleted'] = retention_delete($db, "DELETE FROM sensor_metric WHERE created_at < :cutoff", [':cutoff' => $metricCutoff], $dryRun);

$auditPath = admin_audit_log_path();
if (is_file($auditPath)) {
    $cutoffTs = time() - (ADMIN_AUDIT_RETENTION_DAYS * 86400);
    $kept = [];
    $removed = 0;
    // 감사로그 회전은 동시 append(admin_audit_log: file_put_contents FILE_APPEND|LOCK_EX)와
    // 경합한다. 읽기+필터+재기록 전체를 배타 락(flock LOCK_EX)으로 묶어, 회전 도중
    // 들어온 기록이 유실되지 않게 한다(append 는 락 해제까지 대기 후 새 파일에 추가).
    $fh = fopen($auditPath, 'c+');
    if ($fh !== false && flock($fh, LOCK_EX)) {
        while (($line = fgets($fh)) !== false) {
            $line = rtrim($line, "\r\n");
            if ($line === '') {
                continue;
            }
            $record = json_decode($line, true);
            $recordTs = isset($record['time']) ? strtotime((string)$record['time']) : false;
            if ($recordTs !== false && $recordTs < $cutoffTs) {
                $removed++;
                continue;
            }
            $kept[] = $line;
        }
        if (!$dryRun && $removed > 0) {
            ftruncate($fh, 0);
            rewind($fh);
            fwrite($fh, $kept ? implode(PHP_EOL, $kept) . PHP_EOL : '');
            fflush($fh);
        }
        flock($fh, LOCK_UN);
    }
    if ($fh !== false) {
        fclose($fh);
    }
    $summary['admin_audit']['kept_lines'] = count($kept);
    $summary['admin_audit']['removed_lines'] = $removed;
}

admin_audit_log('retention_cleanup', 'system', true, [
    'mode' => $summary['mode'],
    'sms_matches' => $summary['sms_log']['matched'],
    'sensor_matches' => $summary['sensor_log']['matched'],
    'metric_matches' => $summary['sensor_metric']['matched'],
]);

header('Content-Type: application/json; charset=utf-8');
echo json_encode($summary, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) . PHP_EOL;
