<?php
declare(strict_types=1);

require_once __DIR__ . '/payment_service.php';

function golder_ensure_backup_tables(PDO $master): void
{
    $master->exec(
        "CREATE TABLE IF NOT EXISTS backup_files (
            id BIGINT AUTO_INCREMENT PRIMARY KEY,
            backup_uuid CHAR(36) NOT NULL,
            customer_id BIGINT NOT NULL,
            profile_id VARCHAR(64) NOT NULL,
            backup_type VARCHAR(20) NOT NULL,
            file_path TEXT NOT NULL,
            checksum_sha256 CHAR(64) NOT NULL,
            file_size BIGINT NOT NULL DEFAULT 0,
            profile_count INT NOT NULL DEFAULT 1,
            manifest_json LONGTEXT DEFAULT NULL,
            created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
            restored_at DATETIME DEFAULT NULL,
            restore_note VARCHAR(255) DEFAULT NULL,
            UNIQUE KEY uniq_backup_uuid (backup_uuid),
            KEY idx_customer_profile_created (customer_id, profile_id, created_at)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"
    );

    if (!golder_session_store_column_exists($master, 'backup_files', 'profile_count')) {
        $master->exec('ALTER TABLE backup_files ADD COLUMN profile_count INT NOT NULL DEFAULT 1 AFTER file_size');
    }
    if (!golder_session_store_column_exists($master, 'backup_files', 'manifest_json')) {
        $master->exec('ALTER TABLE backup_files ADD COLUMN manifest_json LONGTEXT DEFAULT NULL AFTER profile_count');
    }
}

function golder_backup_storage_dir(): string
{
    $base = golder_env('GOLDER_BACKUP_DIR', dirname(__DIR__) . '/storage/backups');
    $base = rtrim($base, '/\\');
    if (!is_dir($base)) {
        @mkdir($base, 0775, true);
    }
    return $base;
}

function golder_backup_table_insert_order(array $tableNames): array
{
    $priority = [
        'app_settings',
        'document_counters',
        'users',
        'depots',
        'categories',
        'persons',
        'product_definitions',
        'financial_accounts',
        'taxes',
        'tax_profiles',
        'tax_profile_items',
        'tax_applications',
        'stock_items',
        'barcode_history',
        'stock_movements',
        'financial_transactions',
        'sales',
        'debt_records',
        'customer_history_events',
        'repair_records',
        'scheduled_transactions',
        'service_definitions',
        'service_jobs',
        'agenda_notes',
        'audit_logs',
    ];
    $remaining = [];
    foreach ($tableNames as $name) {
        $tableName = trim((string)$name);
        if ($tableName !== '') {
            $remaining[$tableName] = true;
        }
    }
    $ordered = [];
    foreach ($priority as $tableName) {
        if (isset($remaining[$tableName])) {
            $ordered[] = $tableName;
            unset($remaining[$tableName]);
        }
    }
    $rest = array_keys($remaining);
    sort($rest);
    return array_merge($ordered, $rest);
}

function golder_tenant_profile_tables(PDO $tenant): array
{
    $st = $tenant->query(
        "SELECT DISTINCT TABLE_NAME
         FROM INFORMATION_SCHEMA.COLUMNS
         WHERE TABLE_SCHEMA = DATABASE() AND COLUMN_NAME = 'profile_id'
         ORDER BY TABLE_NAME ASC"
    );
    $tables = [];
    foreach ($st->fetchAll() ?: [] as $row) {
        $name = trim((string)($row['TABLE_NAME'] ?? ''));
        if ($name === '') {
            continue;
        }
        if (in_array($name, ['profiles', 'local_sync_queue', 'sync_state', 'sync_profile_state', 'conflict_logs', 'global_sync_events', 'deleted_profiles', 'profile_sync_jobs'], true)) {
            continue;
        }
        if (!golder_safe_identifier($name)) {
            continue;
        }
        $tables[] = $name;
    }
    return golder_backup_table_insert_order($tables);
}

function golder_table_columns(PDO $db, string $tableName): array
{
    if (!golder_safe_identifier($tableName)) {
        return [];
    }
    $st = $db->prepare(
        "SELECT COLUMN_NAME
         FROM INFORMATION_SCHEMA.COLUMNS
         WHERE TABLE_SCHEMA = DATABASE()
           AND TABLE_NAME = :t"
    );
    $st->execute([':t' => $tableName]);
    $columns = [];
    foreach ($st->fetchAll() ?: [] as $row) {
        $col = trim((string)($row['COLUMN_NAME'] ?? ''));
        if ($col !== '' && golder_safe_identifier($col)) {
            $columns[$col] = true;
        }
    }
    return $columns;
}

function golder_backup_setting_record_id(string $scopeId, string $key): string
{
    $seed = strtolower(trim($scopeId) . ':' . trim($key));
    $digest = sha1($seed);
    return substr($digest, 0, 8) . '-' . substr($digest, 8, 4) . '-' . substr($digest, 12, 4) . '-' . substr($digest, 16, 4) . '-' . substr($digest, 20, 12);
}

function golder_backup_row_record_id(string $tableName, array $row, string $profileId): string
{
    $explicitId = trim((string)($row['id'] ?? ''));
    if ($explicitId !== '') {
        return $explicitId;
    }
    if ($tableName === 'app_settings') {
        return golder_backup_setting_record_id($profileId, (string)($row['setting_key'] ?? ''));
    }
    if ($tableName === 'agenda_notes') {
        return golder_backup_setting_record_id($profileId, 'agenda:' . (string)($row['note_key'] ?? ''));
    }
    if ($tableName === 'user_permissions') {
        return golder_backup_setting_record_id((string)($row['user_id'] ?? ''), 'perm:' . (string)($row['permission_code'] ?? ''));
    }
    if ($tableName === 'user_access_scopes') {
        return golder_backup_setting_record_id(
            (string)($row['user_id'] ?? ''),
            'scope:' . (string)($row['scope_type'] ?? '') . ':' . (string)($row['target_id'] ?? '')
        );
    }
    return golder_backup_setting_record_id($profileId, $tableName . ':' . md5(json_encode($row, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?: '{}'));
}

function golder_backup_prepare_row(PDO $db, string $tableName, array $row, string $profileId, string $fallbackId = ''): array
{
    $columns = golder_table_columns($db, $tableName);
    if (!$columns) {
        return [];
    }
    $safe = [];
    foreach ($row as $key => $value) {
        $column = trim((string)$key);
        if ($column === '' || !golder_safe_identifier($column) || !isset($columns[$column])) {
            continue;
        }
        $safe[$column] = is_resource($value) ? null : $value;
    }

    $rowId = trim((string)($safe['id'] ?? $row['id'] ?? $fallbackId));
    if (isset($columns['id']) && $rowId !== '') {
        $safe['id'] = $rowId;
    }
    if (isset($columns['profile_id']) && trim((string)($safe['profile_id'] ?? '')) === '' && $profileId !== '') {
        $safe['profile_id'] = $profileId;
    }
    return $safe;
}

function golder_collect_profile_rows(PDO $tenant, string $profileId): array
{
    $result = [];
    foreach (golder_tenant_profile_tables($tenant) as $tableName) {
        $st = $tenant->prepare(sprintf('SELECT * FROM `%s` WHERE profile_id = :pid', $tableName));
        $st->execute([':pid' => $profileId]);
        $rows = [];
        foreach ($st->fetchAll() ?: [] as $row) {
            if (is_array($row)) {
                $rows[] = golder_backup_prepare_row(
                    $tenant,
                    $tableName,
                    $row,
                    $profileId,
                    golder_backup_row_record_id($tableName, $row, $profileId)
                );
            }
        }
        $result[$tableName] = $rows;
    }
    return $result;
}

function golder_backup_related_user_rows(PDO $tenant, string $profileId): array
{
    $userIds = [];
    $userRows = $tenant->prepare('SELECT id FROM users WHERE profile_id = :pid');
    $userRows->execute([':pid' => $profileId]);
    foreach ($userRows->fetchAll() ?: [] as $row) {
        $userId = trim((string)($row['id'] ?? ''));
        if ($userId !== '') {
            $userIds[] = $userId;
        }
    }
    if (!$userIds) {
        return ['user_permissions' => [], 'user_access_scopes' => []];
    }
    $placeholders = implode(', ', array_fill(0, count($userIds), '?'));
    $related = ['user_permissions' => [], 'user_access_scopes' => []];
    foreach (array_keys($related) as $tableName) {
        $st = $tenant->prepare(sprintf('SELECT * FROM `%s` WHERE user_id IN (%s)', $tableName, $placeholders));
        $st->execute($userIds);
        foreach ($st->fetchAll() ?: [] as $row) {
            if (is_array($row)) {
                $related[$tableName][] = golder_backup_prepare_row(
                    $tenant,
                    $tableName,
                    $row,
                    $profileId,
                    golder_backup_row_record_id($tableName, $row, $profileId)
                );
            }
        }
    }
    return $related;
}

function golder_build_profile_snapshot(PDO $tenant, string $profileId): array
{
    $profileSt = $tenant->prepare('SELECT * FROM profiles WHERE id = :pid AND COALESCE(is_deleted, 0) = 0 LIMIT 1');
    $profileSt->execute([':pid' => $profileId]);
    $profile = $profileSt->fetch();
    if (!$profile || !is_array($profile)) {
        throw new RuntimeException('Profile not found in tenant db.');
    }

    return [
        'schema_version' => 'profile-sync-v1',
        'profile' => golder_backup_prepare_row($tenant, 'profiles', $profile, $profileId, $profileId),
        'tables' => golder_collect_profile_rows($tenant, $profileId),
        'related' => golder_backup_related_user_rows($tenant, $profileId),
    ];
}

function golder_backup_manual_daily_count(PDO $master, int $customerId, string $profileId = ''): int
{
    golder_ensure_backup_tables($master);
    $sql = "SELECT COUNT(*)
            FROM backup_files
            WHERE customer_id = :cid
              AND backup_type = 'manual'
              AND DATE(created_at) = UTC_DATE()";
    $params = [':cid' => $customerId];
    if (trim($profileId) !== '') {
        $sql .= ' AND (profile_id = :pid OR profile_id = \'MULTI\')';
        $params[':pid'] = $profileId;
    }
    $st = $master->prepare($sql);
    $st->execute($params);
    return (int)$st->fetchColumn();
}

function golder_backup_restore_daily_count(PDO $master, int $customerId): int
{
    golder_ensure_backup_tables($master);
    $st = $master->prepare(
        "SELECT COUNT(*)
         FROM backup_files
         WHERE customer_id = :cid
           AND restored_at IS NOT NULL
           AND DATE(restored_at) = UTC_DATE()"
    );
    $st->execute([':cid' => $customerId]);
    return (int)$st->fetchColumn();
}

function golder_backup_write_bundle(PDO $master, int $customerId, array $manifest, string $backupType, string $primaryProfileId): array
{
    $manifestJson = golder_canonical_json($manifest);
    $checksum = hash('sha256', $manifestJson);
    $payloadJson = golder_canonical_json([
        'manifest' => json_decode($manifestJson, true),
        'checksum_sha256' => $checksum,
    ]);
    $compressed = gzencode($payloadJson, 9);
    if (!is_string($compressed) || $compressed === '') {
        throw new RuntimeException('Could not encode backup payload.');
    }

    $backupUuid = golder_uuid4();
    $customerDir = golder_backup_storage_dir() . DIRECTORY_SEPARATOR . (string)$customerId;
    if (!is_dir($customerDir)) {
        @mkdir($customerDir, 0775, true);
    }
    $baseName = (count($manifest['profiles'] ?? []) > 1) ? 'multi' : preg_replace('/[^a-zA-Z0-9_-]+/', '-', $primaryProfileId);
    $filename = sprintf('%s_%s_%s.golder', gmdate('Ymd_His'), $baseName ?: 'profile', $backupType);
    $fullPath = $customerDir . DIRECTORY_SEPARATOR . $filename;
    if (@file_put_contents($fullPath, $compressed) === false) {
        throw new RuntimeException('Could not write backup file to disk.');
    }

    $ins = $master->prepare(
        'INSERT INTO backup_files
         (backup_uuid, customer_id, profile_id, backup_type, file_path, checksum_sha256, file_size, profile_count, manifest_json, created_at)
         VALUES (:uuid, :cid, :pid, :typ, :path, :chk, :size, :pc, :manifest, UTC_TIMESTAMP())'
    );
    $ins->execute([
        ':uuid' => $backupUuid,
        ':cid' => $customerId,
        ':pid' => (count($manifest['profiles'] ?? []) > 1 ? 'MULTI' : $primaryProfileId),
        ':typ' => $backupType,
        ':path' => $fullPath,
        ':chk' => $checksum,
        ':size' => filesize($fullPath) ?: 0,
        ':pc' => max(1, count($manifest['profiles'] ?? [])),
        ':manifest' => $manifestJson,
    ]);

    return [
        'ok' => true,
        'backup_uuid' => $backupUuid,
        'file_path' => $fullPath,
        'checksum_sha256' => $checksum,
        'file_size' => filesize($fullPath) ?: 0,
        'profile_count' => max(1, count($manifest['profiles'] ?? [])),
        'profiles' => $manifest['profiles'] ?? [],
    ];
}

function golder_create_backup_bundle(PDO $master, array $customer, array $profileIds, string $backupType = 'manual'): array
{
    golder_ensure_backup_tables($master);

    $customerId = (int)($customer['id'] ?? 0);
    if ($customerId <= 0) {
        return ['ok' => false, 'message' => 'Invalid customer.'];
    }
    if (!golder_customer_plan_feature_enabled($customer, 'BACKUP_MANUAL')) {
        return ['ok' => false, 'message' => 'Backup bu plan icin aktif degil.'];
    }
    if ($backupType === 'manual' && golder_backup_manual_daily_count($master, $customerId) >= 3) {
        return ['ok' => false, 'message' => 'Günlük yedek limitine ulaşıldı (3/gün).'];
    }

    $uniqueProfiles = [];
    foreach ($profileIds as $profileId) {
        $id = trim((string)$profileId);
        if ($id !== '') {
            $uniqueProfiles[$id] = true;
        }
    }
    $profileIds = array_keys($uniqueProfiles);
    if (!$profileIds) {
        return ['ok' => false, 'message' => 'At least one profile must be selected.'];
    }

    $tenant = golder_tenant_pdo_from_customer($customer);
    $profiles = [];
    foreach ($profileIds as $profileId) {
        $snapshot = golder_build_profile_snapshot($tenant, $profileId);
        $profiles[] = [
            'profile_id' => (string)($snapshot['profile']['id'] ?? $profileId),
            'profile_name' => (string)($snapshot['profile']['name'] ?? ''),
            'snapshot_schema_version' => 'profile-sync-v1',
            'snapshot_checksum_sha256' => hash('sha256', golder_canonical_json($snapshot)),
            'snapshot' => $snapshot,
        ];
    }

    $manifest = [
        'schema_version' => 'golder-backup-v2',
        'created_at_utc' => golder_utc_now(),
        'backup_type' => $backupType,
        'customer_id' => $customerId,
        'profile_count' => count($profiles),
        'profiles' => $profiles,
    ];

    try {
        $result = golder_backup_write_bundle($master, $customerId, $manifest, $backupType, $profileIds[0]);
        golder_trim_backup_retention($master, $customerId, count($profiles) > 1 ? 'MULTI' : $profileIds[0], 30);
        return $result;
    } catch (Throwable $e) {
        return ['ok' => false, 'message' => $e->getMessage()];
    }
}

function golder_create_profile_backup(PDO $master, array $customer, string $profileId, string $backupType = 'manual'): array
{
    return golder_create_backup_bundle($master, $customer, [$profileId], $backupType);
}

function golder_backup_legacy_manifest_to_bundle(array $manifest): array
{
    $profileId = trim((string)($manifest['profile_id'] ?? ''));
    $profileName = trim((string)($manifest['profile_name'] ?? ''));
    $tables = is_array($manifest['tables'] ?? null) ? $manifest['tables'] : [];
    $profileRow = [
        'id' => $profileId !== '' ? $profileId : golder_uuid4(),
        'name' => $profileName !== '' ? $profileName : 'Yedek Profil',
        'is_deleted' => 0,
    ];
    return [
        'schema_version' => 'golder-backup-v2',
        'created_at_utc' => (string)($manifest['created_at_utc'] ?? golder_utc_now()),
        'backup_type' => (string)($manifest['backup_type'] ?? 'manual'),
        'customer_id' => (int)($manifest['customer_id'] ?? 0),
        'profile_count' => 1,
        'profiles' => [[
            'profile_id' => $profileRow['id'],
            'profile_name' => $profileRow['name'],
            'snapshot_schema_version' => 'profile-sync-v1',
            'snapshot_checksum_sha256' => '',
            'snapshot' => [
                'schema_version' => 'profile-sync-v1',
                'profile' => $profileRow,
                'tables' => $tables,
                'related' => ['user_permissions' => [], 'user_access_scopes' => []],
            ],
        ]],
    ];
}

function golder_backup_read_bundle(string $filePath): array
{
    if (!is_file($filePath)) {
        throw new RuntimeException('Backup file does not exist.');
    }
    $raw = file_get_contents($filePath);
    if (!is_string($raw) || $raw === '') {
        throw new RuntimeException('Could not read backup file.');
    }
    $decoded = gzdecode($raw);
    if (!is_string($decoded) || $decoded === '') {
        throw new RuntimeException('Backup file is not valid gzip content.');
    }
    $payload = json_decode($decoded, true);
    if (!is_array($payload) || !is_array($payload['manifest'] ?? null)) {
        throw new RuntimeException('Backup file format is invalid.');
    }

    $manifest = $payload['manifest'];
    $checksum = trim((string)($payload['checksum_sha256'] ?? ''));
    $manifestJson = golder_canonical_json($manifest);
    $actualChecksum = hash('sha256', $manifestJson);
    if ($checksum === '' || !hash_equals($checksum, $actualChecksum)) {
        throw new RuntimeException('Backup checksum validation failed.');
    }

    $schema = trim((string)($manifest['schema_version'] ?? ''));
    if ($schema === 'goldr-backup-v1') {
        $manifest = golder_backup_legacy_manifest_to_bundle($manifest);
    } elseif ($schema !== 'golder-backup-v2') {
        throw new RuntimeException('Unsupported backup schema.');
    }

    return [
        'manifest' => $manifest,
        'checksum_sha256' => $checksum,
    ];
}

function golder_backup_resolve_profile_name(PDO $tenant, string $baseName, string $dateLabel): string
{
    $baseName = trim($baseName);
    if ($baseName === '') {
        $baseName = 'Profil';
    }
    $st = $tenant->prepare('SELECT COUNT(*) FROM profiles WHERE name = :n AND COALESCE(is_deleted, 0) = 0');
    $st->execute([':n' => $baseName]);
    if ((int)$st->fetchColumn() === 0) {
        return $baseName;
    }

    $candidate = sprintf('%s (%s yedek)', $baseName, $dateLabel);
    $st->execute([':n' => $candidate]);
    if ((int)$st->fetchColumn() === 0) {
        return $candidate;
    }

    $idx = 2;
    while ($idx < 1000) {
        $next = sprintf('%s (%s yedek %d)', $baseName, $dateLabel, $idx);
        $st->execute([':n' => $next]);
        if ((int)$st->fetchColumn() === 0) {
            return $next;
        }
        $idx++;
    }

    return $candidate . ' ' . substr(golder_uuid4(), 0, 8);
}

function golder_backup_walk_remap(mixed $value, array $idMap): mixed
{
    if (is_array($value)) {
        foreach ($value as $key => $item) {
            $value[$key] = golder_backup_walk_remap($item, $idMap);
        }
        return $value;
    }
    if (is_string($value)) {
        $trimmed = trim($value);
        if ($trimmed !== '' && isset($idMap[$trimmed])) {
            return $idMap[$trimmed];
        }
        $decoded = json_decode($value, true);
        if (is_array($decoded)) {
            return json_encode(golder_backup_walk_remap($decoded, $idMap), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        }
    }
    return $value;
}

function golder_backup_insert_row(PDO $tenant, string $tableName, array $row): void
{
    if (!golder_safe_identifier($tableName) || !$row) {
        return;
    }
    $columns = array_keys($row);
    $placeholders = [];
    $params = [];
    foreach ($columns as $idx => $column) {
        $placeholders[] = ':p' . $idx;
        $params[':p' . $idx] = $row[$column];
    }
    $sql = sprintf(
        'INSERT INTO `%s` (%s) VALUES (%s)',
        $tableName,
        implode(', ', array_map(static fn($column) => '`' . $column . '`', $columns)),
        implode(', ', $placeholders)
    );
    $tenant->prepare($sql)->execute($params);
}

function golder_restore_profile_snapshot_as_new_profile(PDO $tenant, array $snapshot, string $dateLabel): array
{
    $profileRow = is_array($snapshot['profile'] ?? null) ? $snapshot['profile'] : [];
    $sourceProfileId = trim((string)($profileRow['id'] ?? ''));
    $sourceProfileName = trim((string)($profileRow['name'] ?? ''));
    if ($sourceProfileId === '') {
        throw new RuntimeException('Snapshot profile id is required.');
    }

    $newProfileId = golder_uuid4();
    $newProfileName = golder_backup_resolve_profile_name($tenant, $sourceProfileName !== '' ? $sourceProfileName : 'Profil', $dateLabel);
    $idMap = [$sourceProfileId => $newProfileId];
    $tables = is_array($snapshot['tables'] ?? null) ? $snapshot['tables'] : [];
    $related = is_array($snapshot['related'] ?? null) ? $snapshot['related'] : [];

    foreach ($tables as $tableName => $rows) {
        if (!is_string($tableName) || !is_array($rows)) {
            continue;
        }
        foreach ($rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $oldId = trim((string)($row['id'] ?? ''));
            if ($oldId !== '' && !isset($idMap[$oldId])) {
                $idMap[$oldId] = golder_uuid4();
            }
        }
    }
    foreach (['user_permissions', 'user_access_scopes'] as $tableName) {
        foreach (($related[$tableName] ?? []) as $row) {
            if (!is_array($row)) {
                continue;
            }
            $oldId = trim((string)($row['id'] ?? ''));
            if ($oldId !== '' && !isset($idMap[$oldId])) {
                $idMap[$oldId] = golder_uuid4();
            }
        }
    }

    $tenant->beginTransaction();
    try {
        $profileInsert = golder_backup_prepare_row($tenant, 'profiles', $profileRow, $newProfileId, $newProfileId);
        $profileInsert['id'] = $newProfileId;
        if (array_key_exists('name', $profileInsert)) {
            $profileInsert['name'] = $newProfileName;
        }
        if (array_key_exists('is_deleted', $profileInsert)) {
            $profileInsert['is_deleted'] = 0;
        }
        golder_backup_insert_row($tenant, 'profiles', $profileInsert);

        foreach (golder_backup_table_insert_order(array_keys($tables)) as $tableName) {
            $rows = $tables[$tableName] ?? [];
            if (!is_array($rows) || !golder_safe_identifier($tableName)) {
                continue;
            }
            $columnsMap = golder_table_columns($tenant, $tableName);
            if (!$columnsMap) {
                continue;
            }
            foreach ($rows as $row) {
                if (!is_array($row)) {
                    continue;
                }
                $prepared = [];
                foreach ($row as $key => $value) {
                    $column = trim((string)$key);
                    if ($column === '' || !golder_safe_identifier($column) || !isset($columnsMap[$column])) {
                        continue;
                    }
                    $prepared[$column] = golder_backup_walk_remap($value, $idMap);
                }
                if (isset($columnsMap['profile_id'])) {
                    $prepared['profile_id'] = $newProfileId;
                }
                if (isset($columnsMap['id']) && trim((string)($row['id'] ?? '')) !== '') {
                    $prepared['id'] = $idMap[trim((string)$row['id'])] ?? golder_uuid4();
                }
                foreach (array_keys($prepared) as $column) {
                    if ($column === 'profile_id') {
                        continue;
                    }
                    if (($column === 'related_id' || str_ends_with($column, '_id')) && is_string($prepared[$column])) {
                        $lookup = trim((string)$prepared[$column]);
                        if ($lookup !== '' && isset($idMap[$lookup])) {
                            $prepared[$column] = $idMap[$lookup];
                        }
                    }
                }
                golder_backup_insert_row($tenant, $tableName, $prepared);
            }
        }

        foreach (['user_permissions', 'user_access_scopes'] as $tableName) {
            $rows = $related[$tableName] ?? [];
            if (!is_array($rows) || !$rows) {
                continue;
            }
            $columnsMap = golder_table_columns($tenant, $tableName);
            if (!$columnsMap) {
                continue;
            }
            foreach ($rows as $row) {
                if (!is_array($row)) {
                    continue;
                }
                $prepared = [];
                foreach ($row as $key => $value) {
                    $column = trim((string)$key);
                    if ($column === '' || !golder_safe_identifier($column) || !isset($columnsMap[$column])) {
                        continue;
                    }
                    $prepared[$column] = golder_backup_walk_remap($value, $idMap);
                }
                if (isset($columnsMap['id']) && trim((string)($row['id'] ?? '')) !== '') {
                    $prepared['id'] = $idMap[trim((string)$row['id'])] ?? golder_uuid4();
                }
                if (isset($columnsMap['user_id'])) {
                    $oldUserId = trim((string)($row['user_id'] ?? ''));
                    if ($oldUserId !== '' && isset($idMap[$oldUserId])) {
                        $prepared['user_id'] = $idMap[$oldUserId];
                    }
                }
                golder_backup_insert_row($tenant, $tableName, $prepared);
            }
        }

        $tenant->commit();
    } catch (Throwable $e) {
        if ($tenant->inTransaction()) {
            $tenant->rollBack();
        }
        throw $e;
    }

    return [
        'source_profile_id' => $sourceProfileId,
        'profile_id' => $newProfileId,
        'profile_name' => $newProfileName,
    ];
}

function golder_restore_profile_backup(PDO $master, array $customer, string $profileId, string $filePath): array
{
    golder_ensure_backup_tables($master);

    $customerId = (int)($customer['id'] ?? 0);
    if ($customerId <= 0) {
        return ['ok' => false, 'message' => 'Invalid customer.'];
    }
    if (!golder_customer_plan_feature_enabled($customer, 'BACKUP_MANUAL')) {
        return ['ok' => false, 'message' => 'Geri yukleme bu plan icin aktif degil.'];
    }
    if (golder_backup_restore_daily_count($master, $customerId) >= 3) {
        return ['ok' => false, 'message' => 'Günlük geri yükleme limitine ulaşıldı (3/gün).'];
    }

    try {
        $bundle = golder_backup_read_bundle($filePath);
    } catch (Throwable $e) {
        return ['ok' => false, 'message' => $e->getMessage()];
    }

    $manifest = $bundle['manifest'];
    $tenant = golder_tenant_pdo_from_customer($customer);
    $dateLabel = date('Y-m-d');
    $restoredProfiles = [];

    try {
        foreach (($manifest['profiles'] ?? []) as $profileItem) {
            if (!is_array($profileItem)) {
                continue;
            }
            $snapshot = is_array($profileItem['snapshot'] ?? null) ? $profileItem['snapshot'] : [];
            if (!$snapshot) {
                continue;
            }
            $restoredProfiles[] = golder_restore_profile_snapshot_as_new_profile($tenant, $snapshot, $dateLabel);
        }
    } catch (Throwable $e) {
        return ['ok' => false, 'message' => 'Restore failed: ' . $e->getMessage()];
    }

    $master->prepare(
        'UPDATE backup_files
         SET restored_at = UTC_TIMESTAMP(),
             restore_note = :n
         WHERE file_path = :p
           AND customer_id = :cid'
    )->execute([
        ':n' => 'profile_restore_new_profiles',
        ':p' => $filePath,
        ':cid' => $customerId,
    ]);

    return [
        'ok' => true,
        'message' => 'Backup restore completed.',
        'restored_profiles' => $restoredProfiles,
        'profile_count' => count($restoredProfiles),
    ];
}

function golder_list_backups(PDO $master, int $customerId, ?string $profileId = null, int $limit = 50): array
{
    golder_ensure_backup_tables($master);
    $limit = max(1, min(200, $limit));

    $sql = 'SELECT backup_uuid, profile_id, backup_type, file_path, checksum_sha256, file_size, profile_count, manifest_json, created_at, restored_at
            FROM backup_files
            WHERE customer_id = :cid';
    $params = [':cid' => $customerId];
    if ($profileId !== null && trim($profileId) !== '') {
        $sql .= ' AND (profile_id = :pid OR profile_id = \'MULTI\')';
        $params[':pid'] = $profileId;
    }
    $sql .= ' ORDER BY created_at DESC LIMIT ' . (int)$limit;
    $st = $master->prepare($sql);
    $st->execute($params);
    $rows = $st->fetchAll() ?: [];

    foreach ($rows as &$row) {
        $profiles = [];
        $manifestRaw = trim((string)($row['manifest_json'] ?? ''));
        if ($manifestRaw !== '') {
            $manifest = json_decode($manifestRaw, true);
            if (is_array($manifest)) {
                foreach (($manifest['profiles'] ?? []) as $profile) {
                    if (!is_array($profile)) {
                        continue;
                    }
                    $profiles[] = [
                        'profile_id' => trim((string)($profile['profile_id'] ?? '')),
                        'profile_name' => trim((string)($profile['profile_name'] ?? '')),
                    ];
                }
            }
        }
        $row['profiles'] = $profiles;
        $row['profile_names'] = implode(', ', array_values(array_filter(array_map(static fn(array $profile): string => trim((string)($profile['profile_name'] ?? '')), $profiles))));
        if ($row['profile_names'] === '') {
            $row['profile_names'] = trim((string)($row['profile_id'] ?? ''));
        }
    }
    unset($row);

    return $rows;
}

function golder_trim_backup_retention(PDO $master, int $customerId, string $profileId, int $keepFiles = 30): int
{
    golder_ensure_backup_tables($master);
    $keepFiles = max(1, $keepFiles);
    if ($customerId <= 0) {
        return 0;
    }

    $st = $master->prepare(
        'SELECT id, file_path
         FROM backup_files
         WHERE customer_id = :cid
           AND profile_id = :pid
         ORDER BY created_at DESC, id DESC'
    );
    $st->execute([':cid' => $customerId, ':pid' => $profileId]);
    $rows = $st->fetchAll() ?: [];
    if (count($rows) <= $keepFiles) {
        return 0;
    }

    $deleted = 0;
    $deleteSt = $master->prepare('DELETE FROM backup_files WHERE id = :id');
    foreach (array_slice($rows, $keepFiles) as $row) {
        $deleteSt->execute([':id' => (int)($row['id'] ?? 0)]);
        if ($deleteSt->rowCount() > 0) {
            $deleted++;
            $path = (string)($row['file_path'] ?? '');
            if ($path !== '' && is_file($path)) {
                @unlink($path);
            }
        }
    }
    return $deleted;
}


