<?php

function audit(
    PDO $pdo,
    int $user_id,
    string $action,
    string $module,
    int $record_id,
    string $description,
    $before = null,
    $after = null
) {
    $beforeJson = $before ? json_encode($before) : null;
    $afterJson = $after ? json_encode($after) : null;
    $ip = $_SERVER['REMOTE_ADDR'] ?? null;
    $now = gmdate('Y-m-d H:i:s');

    $prevHash = null;
    $lastStmt = $pdo->query("SELECT hash FROM audit_logs ORDER BY id DESC LIMIT 1 FOR UPDATE");
    $lastRow = $lastStmt->fetch(PDO::FETCH_ASSOC);
    if ($lastRow && !empty($lastRow['hash'])) {
        $prevHash = $lastRow['hash'];
    }

    $hashData = implode('|', [
        $prevHash ?? '',
        $user_id,
        $action,
        $module,
        $record_id,
        $description,
        $beforeJson ?? '',
        $afterJson ?? '',
        $ip ?? '',
        $now
    ]);
    $hash = hash('sha256', $hashData);

    $stmt = $pdo->prepare("
        INSERT INTO audit_logs
        (user_id, action, module, record_id, description, before_data, after_data, ip_address, prev_hash, hash, created_at)
        VALUES (?,?,?,?,?,?,?,?,?,?,?)
    ");
    $stmt->execute([
        $user_id,
        $action,
        $module,
        $record_id,
        $description,
        $beforeJson,
        $afterJson,
        $ip,
        $prevHash,
        $hash,
        $now
    ]);
}

function verifyAuditIntegrity(PDO $pdo, ?int $sinceId = null): array
{
    if ($sinceId) {
        $stmt = $pdo->prepare("SELECT * FROM audit_logs WHERE id >= ? ORDER BY id ASC");
        $stmt->execute([$sinceId]);
    } else {
        $stmt = $pdo->query("SELECT * FROM audit_logs ORDER BY id ASC");
    }
    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

    $results = [];
    $expectedPrevHash = null;
    $prevId = null;

    foreach ($rows as $row) {
        $expectedHash = hash('sha256', implode('|', [
            $expectedPrevHash ?? '',
            $row['user_id'],
            $row['action'],
            $row['module'],
            $row['record_id'],
            $row['description'] ?? '',
            $row['before_data'] ?? '',
            $row['after_data'] ?? '',
            $row['ip_address'] ?? '',
            $row['created_at']
        ]));

        $hashMatch = hash_equals($expectedHash, $row['hash'] ?? '');
        $prevMatch = $expectedPrevHash === ($row['prev_hash'] ?? null) || ($expectedPrevHash === null && ($row['prev_hash'] ?? null) === null);

        $results[] = [
            'id' => (int)$row['id'],
            'valid' => $hashMatch && $prevMatch,
            'hash_mismatch' => !$hashMatch,
            'chain_broken' => !$prevMatch,
            'expected_hash' => $expectedHash,
            'stored_hash' => $row['hash'],
            'expected_prev_hash' => $expectedPrevHash,
            'stored_prev_hash' => $row['prev_hash'],
            'description' => substr($row['description'] ?? '', 0, 100),
            'created_at' => $row['created_at']
        ];

        if ($hashMatch && $prevMatch && !empty($row['hash'])) {
            $expectedPrevHash = $row['hash'];
        } else {
            $expectedPrevHash = null;
        }
        $prevId = $row['id'];
    }

    $valid = count(array_filter($results, fn($r) => $r['valid']));
    return [
        'total_checked' => count($results),
        'valid' => $valid,
        'tampered' => count($results) - $valid,
        'details' => $results
    ];
}

function backfillAuditHashes(PDO $pdo): array
{
    $count = 0;
    $rows = $pdo->query("SELECT id FROM audit_logs WHERE hash IS NULL ORDER BY id ASC")->fetchAll(PDO::FETCH_ASSOC);
    foreach ($rows as $r) {
        $stmt = $pdo->prepare("SELECT * FROM audit_logs WHERE id = ?");
        $stmt->execute([(int)$r['id']]);
        $row = $stmt->fetch(PDO::FETCH_ASSOC);
        if (!$row) continue;

        $prevHash = null;
        if ($count > 0) {
            $stmt = $pdo->prepare("SELECT hash FROM audit_logs WHERE id < ? AND hash IS NOT NULL ORDER BY id DESC LIMIT 1");
            $stmt->execute([(int)$row['id']]);
            $prev = $stmt->fetch(PDO::FETCH_ASSOC);
            if ($prev) $prevHash = $prev['hash'];
        }

        $hash = hash('sha256', implode('|', [
            $prevHash ?? '',
            $row['user_id'],
            $row['action'],
            $row['module'],
            $row['record_id'],
            $row['description'] ?? '',
            $row['before_data'] ?? '',
            $row['after_data'] ?? '',
            $row['ip_address'] ?? '',
            $row['created_at']
        ]));

        $pdo->prepare("UPDATE audit_logs SET prev_hash = ?, hash = ? WHERE id = ?")->execute([$prevHash, $hash, $row['id']]);
        $count++;
    }
    return ['backfilled' => $count];
}

function runRetentionCleanup(PDO $pdo): array
{
    $totalDeleted = 0;
    $totalArchived = 0;
    $details = [];

    $policies = $pdo->query("SELECT * FROM audit_retention_policies WHERE is_active = 1")->fetchAll(PDO::FETCH_ASSOC);

    foreach ($policies as $policy) {
        $module = $policy['module'];
        $action = $policy['action'];
        $days = (int)$policy['retention_days'];
        $archiveAction = $policy['archive_action'];
        $cutoff = date('Y-m-d H:i:s', strtotime("-{$days} days"));

        $where = ["created_at < :cutoff"];
        $params = ['cutoff' => $cutoff];
        if ($module !== 'default') {
            $where[] = 'module = :module';
            $params['module'] = $module;
        }
        if ($action) {
            $where[] = 'action = :action';
            $params['action'] = $action;
        }

        $whereSql = implode(' AND ', $where);

        if ($archiveAction === 'anonymize') {
            $stmt = $pdo->prepare("UPDATE audit_logs SET description = '[ANONYMIZED]', before_data = NULL, after_data = NULL, ip_address = NULL WHERE {$whereSql} AND deleted_at IS NULL");
            $stmt->execute($params);
            $affected = $stmt->rowCount();
            $totalArchived += $affected;
            $details[] = "Anonymized {$affected} records for module={$module}, action=" . ($action ?: 'any');
        } elseif ($archiveAction === 'archive') {
            $stmt = $pdo->prepare("UPDATE audit_logs SET deleted_at = NOW() WHERE {$whereSql} AND deleted_at IS NULL");
            $stmt->execute($params);
            $affected = $stmt->rowCount();
            $totalArchived += $affected;
            $details[] = "Archived (soft-deleted) {$affected} records for module={$module}, action=" . ($action ?: 'any');
        } else {
            $stmt = $pdo->prepare("DELETE FROM audit_logs WHERE {$whereSql} AND deleted_at IS NULL");
            $stmt->execute($params);
            $affected = $stmt->rowCount();
            $totalDeleted += $affected;
            $details[] = "Deleted {$affected} records for module={$module}, action=" . ($action ?: 'any');
        }
    }

    $pdo->prepare("INSERT INTO audit_retention_log (records_deleted, records_archived, details) VALUES (?, ?, ?)")
        ->execute([$totalDeleted, $totalArchived, json_encode($details)]);

    return [
        'records_deleted' => $totalDeleted,
        'records_archived' => $totalArchived,
        'details' => $details
    ];
}
