<?php
declare(strict_types=1);

require_once dirname(__DIR__, 2) . '/common/master_db.php';
require_once dirname(__DIR__, 2) . '/common/plan_guard.php';
require_once dirname(__DIR__) . '/_bootstrap.php';

function sync_api_log(string $message, array $context = []): void
{
    $line = '[' . date('Y-m-d H:i:s') . '] ' . trim($message);
    if ($context) {
        $json = json_encode($context, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        if ($json !== false) {
            $line .= ' ' . $json;
        }
    }
    $line .= PHP_EOL;
    @file_put_contents(dirname(__DIR__, 2) . '/sync_events.log', $line, FILE_APPEND);
}

function sync_api_json_response(array $payload, int $statusCode = 200): void
{
    http_response_code($statusCode);
    header('Content-Type: application/json; charset=utf-8');
    echo json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    exit;
}

function sync_api_json_error(int $statusCode, string $code, string $message, array $extra = []): void
{
    sync_api_json_response(array_merge([
        'status' => 'error',
        'code' => $code,
        'message' => $message,
    ], $extra), $statusCode);
}

function sync_api_fail_from_exception(Throwable $e, string $code = 'SERVER_ERROR', int $statusCode = 500): void
{
    sync_api_log($code, ['error' => $e->getMessage()]);
    sync_api_json_error($statusCode, $code, $e->getMessage());
}

function sync_api_pdo_connect(string $host, int $port, string $db, string $user, string $pass): PDO
{
    return new PDO(
        "mysql:host={$host};port={$port};dbname={$db};charset=utf8mb4",
        $user,
        $pass,
        [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_EMULATE_PREPARES => false,
        ]
    );
}

function sync_api_master_pdo(): PDO
{
    static $pdo = null;
    if ($pdo instanceof PDO) {
        return $pdo;
    }
    $cfg = golder_master_db_config();
    $pdo = sync_api_pdo_connect(
        (string)$cfg['host'],
        (int)$cfg['port'],
        (string)$cfg['name'],
        (string)$cfg['user'],
        (string)$cfg['pass']
    );
    return $pdo;
}

function sync_api_require_profile_sync_customer(int $customerId): array
{
    $master = sync_api_master_pdo();
    $customer = golder_enforce_customer_plan($master, $customerId);
    if (!$customer) {
        sync_api_json_response(['status' => 'error', 'code' => 'CUSTOMER_NOT_FOUND'], 404);
    }
    $effective = golder_customer_effective_plan($customer);
    $planType = (string)($effective['plan_type'] ?? '');
    if (!golder_plan_feature_enabled($master, $planType, 'PROFILE_SYNC')) {
        sync_api_json_response(
            [
                'status' => 'error',
                'code' => 'PROFILE_SYNC_DISABLED_FOR_PLAN',
                'message' => 'Profile sync is disabled for this plan.',
            ],
            403
        );
    }
    if ((string)($effective['sync_status'] ?? 'disabled') !== 'active') {
        sync_api_json_response(
            [
                'status' => 'error',
                'code' => 'SYNC_EXPIRED',
                'message' => 'Sync suresi doldu. Masaustu yerel modda calismaya devam edebilir.',
                'sync_status' => (string)($effective['sync_status'] ?? 'expired'),
                'sync_expiry_date' => (string)($effective['sync_expiry_date'] ?? ''),
            ],
            403
        );
    }
    return is_array($customer) ? $customer : [];
}

function sync_api_uuid4(): string
{
    $b = random_bytes(16);
    $b[6] = chr((ord($b[6]) & 0x0f) | 0x40);
    $b[8] = chr((ord($b[8]) & 0x3f) | 0x80);
    $h = bin2hex($b);
    return substr($h, 0, 8) . '-' . substr($h, 8, 4) . '-' . substr($h, 12, 4) . '-' . substr($h, 16, 4) . '-' . substr($h, 20, 12);
}

function sync_api_is_safe_table_name(string $tableName): bool
{
    return $tableName !== '' && preg_match('/^[A-Za-z0-9_]+$/', $tableName) === 1;
}

function sync_api_ensure_global_events_table(PDO $db): void
{
    static $done = [];
    $objKey = spl_object_id($db);
    if (!empty($done[$objKey])) {
        return;
    }

    if (!$db->inTransaction()) {
        try {
            $db->exec(
                "CREATE TABLE IF NOT EXISTS global_sync_events (
                    id BIGINT AUTO_INCREMENT PRIMARY KEY,
                    transaction_uuid CHAR(36),
                    device_id VARCHAR(50),
                    profile_id CHAR(36),
                    table_name VARCHAR(50),
                    action_type VARCHAR(20),
                    record_id CHAR(36),
                    payload_json JSON,
                    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
                    KEY idx_profile_created (profile_id, created_at),
                    KEY idx_tx_uuid (transaction_uuid)
                ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"
            );
        } catch (Throwable $ignore) {
        }
    }
    $done[$objKey] = true;
}

function sync_api_ensure_deleted_profiles_table(PDO $db): bool
{
    static $done = [];
    $objKey = spl_object_id($db);
    if (!empty($done[$objKey])) {
        return true;
    }

    if (!$db->inTransaction()) {
        try {
            $db->exec(
                "CREATE TABLE IF NOT EXISTS deleted_profiles (
                    profile_id CHAR(36) PRIMARY KEY,
                    profile_name VARCHAR(100) NULL,
                    transaction_uuid CHAR(36) NULL,
                    device_id VARCHAR(50) NULL,
                    deleted_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
                    KEY idx_deleted_profiles_deleted_at (deleted_at)
                ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"
            );
        } catch (Throwable $e) {
            return false;
        }
    }
    $done[$objKey] = true;
    return true;
}

function sync_api_deleted_profile_row(PDO $db, string $profileId): ?array
{
    $profileId = trim($profileId);
    if ($profileId === '') {
        return null;
    }
    if (!sync_api_ensure_deleted_profiles_table($db)) {
        return null;
    }
    $st = $db->prepare(
        "SELECT profile_id, profile_name, transaction_uuid, device_id, deleted_at
         FROM deleted_profiles
         WHERE profile_id = :pid
         LIMIT 1"
    );
    $st->execute([':pid' => $profileId]);
    $row = $st->fetch();
    return is_array($row) ? $row : null;
}

function sync_api_record_profile_tombstone(
    PDO $db,
    string $profileId,
    string $profileName = '',
    string $transactionUuid = '',
    string $deviceId = ''
): void {
    if (!sync_api_ensure_deleted_profiles_table($db)) {
        return;
    }
    $db->prepare(
        "INSERT INTO deleted_profiles (profile_id, profile_name, transaction_uuid, device_id, deleted_at)
         VALUES (:pid, :pn, :tx, :dev, UTC_TIMESTAMP())
         ON DUPLICATE KEY UPDATE
            profile_name = VALUES(profile_name),
            transaction_uuid = VALUES(transaction_uuid),
            device_id = VALUES(device_id),
            deleted_at = VALUES(deleted_at)"
    )->execute([
        ':pid' => trim($profileId),
        ':pn' => trim($profileName),
        ':tx' => trim($transactionUuid),
        ':dev' => trim($deviceId),
    ]);
}

function sync_api_tx_event_summary(PDO $db, string $transactionUuid): array
{
    $tx = trim($transactionUuid);
    if ($tx === '') {
        return ['event_count' => 0, 'max_event_id' => 0];
    }
    sync_api_ensure_global_events_table($db);
    $st = $db->prepare(
        "SELECT COUNT(*) AS event_count, COALESCE(MAX(id), 0) AS max_event_id
         FROM global_sync_events
         WHERE transaction_uuid = :tx"
    );
    $st->execute([':tx' => $tx]);
    $row = $st->fetch() ?: [];
    return [
        'event_count' => (int)($row['event_count'] ?? 0),
        'max_event_id' => (int)($row['max_event_id'] ?? 0),
    ];
}

function sync_api_table_columns(PDO $db, string $tableName): array
{
    static $cache = [];
    if (!sync_api_is_safe_table_name($tableName)) {
        return [];
    }
    $cacheKey = spl_object_id($db) . ':' . $tableName;
    if (array_key_exists($cacheKey, $cache)) {
        return $cache[$cacheKey];
    }
    try {
        $st = $db->prepare(
            "SELECT COLUMN_NAME
             FROM INFORMATION_SCHEMA.COLUMNS
             WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = :t"
        );
        $st->execute([':t' => $tableName]);
        $rows = $st->fetchAll() ?: [];
        $cols = [];
        foreach ($rows as $row) {
            $col = trim((string)($row['COLUMN_NAME'] ?? ''));
            if ($col !== '') {
                $cols[$col] = true;
            }
        }
        $cache[$cacheKey] = $cols;
        return $cols;
    } catch (Throwable $_ignored) {
        $cache[$cacheKey] = [];
        return [];
    }
}

function sync_api_table_has_column(PDO $db, string $tableName, string $columnName): bool
{
    $cols = sync_api_table_columns($db, $tableName);
    return isset($cols[$columnName]);
}

function sync_api_normalize_value($value)
{
    if (is_bool($value)) {
        return $value ? 1 : 0;
    }
    if (is_array($value)) {
        $json = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        return ($json === false) ? '{}' : $json;
    }
    return $value;
}

function sync_api_prepare_row_data(
    PDO $db,
    string $tableName,
    array $row,
    string $profileId,
    string $fallbackId = ''
): array {
    $cols = sync_api_table_columns($db, $tableName);
    if (!$cols) {
        return [];
    }
    $safe = [];
    foreach ($row as $key => $value) {
        $col = trim((string)$key);
        if (!sync_api_is_safe_table_name($col) || !isset($cols[$col])) {
            continue;
        }
        $safe[$col] = sync_api_normalize_value($value);
    }

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

    if (isset($cols['profile_id']) && trim((string)($safe['profile_id'] ?? '')) === '' && $profileId !== '') {
        $safe['profile_id'] = $profileId;
    }

    return $safe;
}

function sync_api_upsert_row(PDO $db, string $tableName, array $rowData): void
{
    if (!$rowData || !sync_api_is_safe_table_name($tableName)) {
        return;
    }
    $columns = array_keys($rowData);
    if (!$columns) {
        return;
    }

    $insertCols = [];
    $placeholders = [];
    $params = [];
    foreach ($columns as $idx => $col) {
        if (!sync_api_is_safe_table_name((string)$col)) {
            continue;
        }
        $insertCols[] = "`{$col}`";
        $ph = ":p{$idx}";
        $placeholders[] = $ph;
        $params[$ph] = $rowData[$col];
    }
    if (!$insertCols) {
        return;
    }

    $sql = "INSERT INTO `{$tableName}` (" . implode(', ', $insertCols) . ") VALUES (" . implode(', ', $placeholders) . ")";
    $updateCols = array_values(array_filter($columns, static fn($c) => (string)$c !== 'id'));
    if ($updateCols) {
        $updates = [];
        foreach ($updateCols as $col) {
            if (!sync_api_is_safe_table_name((string)$col)) {
                continue;
            }
            $updates[] = "`{$col}` = VALUES(`{$col}`)";
        }
        if ($updates) {
            $sql .= " ON DUPLICATE KEY UPDATE " . implode(', ', $updates);
        }
    }

    $st = $db->prepare($sql);
    $st->execute($params);
}

function sync_api_soft_delete_or_delete_row(
    PDO $db,
    string $tableName,
    string $recordId,
    string $profileId = '',
    array $rowHint = []
): void {
    if (!sync_api_is_safe_table_name($tableName)) {
        return;
    }
    $recordId = trim($recordId);
    $hasId = sync_api_table_has_column($db, $tableName, 'id');
    $hasProfile = sync_api_table_has_column($db, $tableName, 'profile_id');
    $hasSoftDelete = sync_api_table_has_column($db, $tableName, 'is_deleted');

    if ($hasId && $recordId !== '') {
        if ($hasSoftDelete) {
            if ($hasProfile && $profileId !== '') {
                $st = $db->prepare("UPDATE `{$tableName}` SET is_deleted = 1 WHERE id = :id AND profile_id = :pid");
                $st->execute([':id' => $recordId, ':pid' => $profileId]);
                return;
            }
            $st = $db->prepare("UPDATE `{$tableName}` SET is_deleted = 1 WHERE id = :id");
            $st->execute([':id' => $recordId]);
            return;
        }

        if ($hasProfile && $profileId !== '') {
            $st = $db->prepare("DELETE FROM `{$tableName}` WHERE id = :id AND profile_id = :pid");
            $st->execute([':id' => $recordId, ':pid' => $profileId]);
            return;
        }
        $st = $db->prepare("DELETE FROM `{$tableName}` WHERE id = :id");
        $st->execute([':id' => $recordId]);
        return;
    }

    // id kolonu olmayan tablolar icin dogal anahtar fallback'i.
    if ($tableName === 'user_permissions') {
        $userId = trim((string)($rowHint['user_id'] ?? ''));
        $permCode = trim((string)($rowHint['permission_code'] ?? ''));
        if ($userId !== '' && $permCode !== '') {
            $st = $db->prepare("DELETE FROM `user_permissions` WHERE user_id = :uid AND permission_code = :pc");
            $st->execute([':uid' => $userId, ':pc' => $permCode]);
        }
        return;
    }
    if ($tableName === 'user_access_scopes') {
        $userId = trim((string)($rowHint['user_id'] ?? ''));
        $scopeType = trim((string)($rowHint['scope_type'] ?? ''));
        $targetId = trim((string)($rowHint['target_id'] ?? ''));
        if ($userId !== '' && $scopeType !== '' && $targetId !== '') {
            $st = $db->prepare("DELETE FROM `user_access_scopes` WHERE user_id = :uid AND scope_type = :st AND target_id = :tid");
            $st->execute([':uid' => $userId, ':st' => $scopeType, ':tid' => $targetId]);
        }
    }
}

function sync_api_apply_event_to_table(
    PDO $db,
    string $tableName,
    string $actionType,
    string $recordId,
    string $profileId,
    array $payload
): void {
    if (!sync_api_is_safe_table_name($tableName)) {
        return;
    }
    $action = strtoupper(trim($actionType));
    $before = is_array($payload['before'] ?? null) ? $payload['before'] : [];
    $after = is_array($payload['after'] ?? null) ? $payload['after'] : [];
    $resolvedProfile = trim((string)($payload['profile_id'] ?? $profileId));

    if (in_array($action, ['ADD', 'UPDATE'], true)) {
        if ($after) {
            $row = sync_api_prepare_row_data($db, $tableName, $after, $resolvedProfile, $recordId);
            if ($row) {
                sync_api_upsert_row($db, $tableName, $row);
            }
        }
        return;
    }

    if ($action === 'DELETE') {
        if ($after) {
            $row = sync_api_prepare_row_data($db, $tableName, $after, $resolvedProfile, $recordId);
            if ($row) {
                sync_api_upsert_row($db, $tableName, $row);
            }
            return;
        }
        $targetId = trim((string)($recordId !== '' ? $recordId : ($before['id'] ?? '')));
        sync_api_soft_delete_or_delete_row($db, $tableName, $targetId, $resolvedProfile, $before);
        return;
    }

    if ($action === 'ROLLBACK') {
        $originalAction = strtoupper(trim((string)(($payload['meta'] ?? [])['original_action'] ?? '')));
        if ($originalAction === 'ADD') {
            $targetId = trim((string)($recordId !== '' ? $recordId : ($after['id'] ?? '')));
            sync_api_soft_delete_or_delete_row($db, $tableName, $targetId, $resolvedProfile, $after);
            return;
        }
        if ($before) {
            $row = sync_api_prepare_row_data($db, $tableName, $before, $resolvedProfile, $recordId);
            if ($row) {
                sync_api_upsert_row($db, $tableName, $row);
            }
        }
    }
}

function sync_api_profile_exists(PDO $tenant, string $profileId): bool
{
    $profileId = trim($profileId);
    if ($profileId === '') {
        return false;
    }
    $st = $tenant->prepare('SELECT id FROM profiles WHERE id = :pid AND COALESCE(is_deleted, 0) = 0 LIMIT 1');
    $st->execute([':pid' => $profileId]);
    return (bool)$st->fetch();
}

function sync_api_delete_profile_rows(PDO $db, string $profileId): void
{
    $profileId = trim($profileId);
    if ($profileId === '') {
        return;
    }
    $st = $db->query(
        "SELECT DISTINCT c.TABLE_NAME
         FROM INFORMATION_SCHEMA.COLUMNS c
         JOIN INFORMATION_SCHEMA.TABLES t
           ON t.TABLE_SCHEMA = c.TABLE_SCHEMA AND t.TABLE_NAME = c.TABLE_NAME
         WHERE c.TABLE_SCHEMA = DATABASE()
           AND c.COLUMN_NAME = 'profile_id'
           AND t.TABLE_TYPE = 'BASE TABLE'"
    );
    $rows = $st->fetchAll() ?: [];
    $tables = [];
    foreach ($rows as $row) {
        $tableName = trim((string)($row['TABLE_NAME'] ?? ''));
        if ($tableName === '' || !sync_api_is_safe_table_name($tableName)) {
            continue;
        }
        if (in_array($tableName, ['profiles', 'deleted_profiles'], true)) {
            continue;
        }
        $tables[$tableName] = true;
    }

    $priority = ['financial_transactions', 'financial_accounts', 'service_jobs', 'service_definitions'];
    $ordered = [];
    foreach ($priority as $tableName) {
        if (isset($tables[$tableName])) {
            $ordered[] = $tableName;
            unset($tables[$tableName]);
        }
    }
    $usersPresent = isset($tables['users']);
    if ($usersPresent) {
        unset($tables['users']);
    }
    $rest = array_keys($tables);
    sort($rest);
    $ordered = array_merge($ordered, $rest);
    if ($usersPresent) {
        $ordered[] = 'users';
    }

    foreach ($ordered as $tableName) {
        $db->prepare(sprintf('DELETE FROM `%s` WHERE profile_id = :pid', $tableName))->execute([':pid' => $profileId]);
    }
    $db->prepare('DELETE FROM profiles WHERE id = :pid')->execute([':pid' => $profileId]);
}

function sync_api_assert_table_scope(PDO $tenant, string $tableName, string $profileId, array $payload, string $recordId): void
{
    $tableName = trim($tableName);
    $profileId = trim($profileId);
    if ($profileId === '' || $tableName === '') {
        sync_api_json_error(400, 'INVALID_REQUEST', 'profile_id and table_name are required.');
    }

    if ($tableName === 'profiles') {
        $candidate = trim((string)(($payload['after'] ?? [])['id'] ?? ($payload['before'] ?? [])['id'] ?? $recordId));
        if ($candidate !== '' && $candidate !== $profileId) {
            sync_api_json_error(403, 'FORBIDDEN', 'profiles row is outside requested profile scope.');
        }
        return;
    }

    if (!sync_api_table_has_column($tenant, $tableName, 'profile_id')) {
        return;
    }

    $candidates = [
        trim((string)($payload['profile_id'] ?? '')),
        trim((string)(($payload['after'] ?? [])['profile_id'] ?? '')),
        trim((string)(($payload['before'] ?? [])['profile_id'] ?? '')),
    ];
    foreach ($candidates as $candidate) {
        if ($candidate !== '' && $candidate !== $profileId) {
            sync_api_json_error(403, 'FORBIDDEN', 'payload profile_id does not match request profile_id.');
        }
    }
}

function sync_api_profile_tenant_pdo(
    string $profileId,
    string $targetDbName = '',
    int $customerId = 0,
    bool $requireProfile = true
): ?array
{
    $profileId = trim($profileId);
    $targetDbName = trim($targetDbName);
    if ($profileId === '' && $targetDbName === '') {
        return null;
    }
    $master = sync_api_master_pdo();
    if ($customerId > 0) {
        $st = $master->prepare(
            "SELECT mysql_db_name, mysql_db_user, mysql_db_pass
             FROM customers
             WHERE id = :id
             LIMIT 1"
        );
        $st->execute([':id' => $customerId]);
        $single = $st->fetch();
        $rows = $single ? [$single] : [];
    } else {
        $rows = $master->query(
            "SELECT mysql_db_name, mysql_db_user, mysql_db_pass
             FROM customers
             WHERE mysql_db_name IS NOT NULL AND mysql_db_name <> ''
               AND mysql_db_user IS NOT NULL AND mysql_db_user <> ''"
        )->fetchAll() ?: [];
    }

    foreach ($rows as $row) {
        $dbName = trim((string)($row['mysql_db_name'] ?? ''));
        $dbUser = trim((string)($row['mysql_db_user'] ?? ''));
        $dbPass = (string)($row['mysql_db_pass'] ?? '');
        if ($dbName === '' || $dbUser === '') {
            continue;
        }

        if ($targetDbName !== '' && $targetDbName !== $dbName) {
            continue;
        }

        try {
            $cfg = golder_master_db_config();
            $tenant = sync_api_pdo_connect(
                (string)$cfg['host'],
                (int)$cfg['port'],
                $dbName,
                $dbUser,
                $dbPass
            );

            if ($targetDbName === $dbName) {
                sync_api_ensure_global_events_table($tenant);
                if ($requireProfile && $profileId !== '' && !sync_api_profile_exists($tenant, $profileId)) {
                    return null;
                }
                return [
                    'pdo' => $tenant,
                    'db_name' => $dbName,
                ];
            }

            $st = $tenant->prepare("SELECT id FROM profiles WHERE id = :pid AND COALESCE(is_deleted, 0) = 0 LIMIT 1");
            $st->execute([':pid' => $profileId]);
            if (!$requireProfile || $st->fetch()) {
                sync_api_ensure_global_events_table($tenant);
                return [
                    'pdo' => $tenant,
                    'db_name' => $dbName,
                ];
            }
        } catch (Throwable $_ignored) {
        }
    }
    return null;
}

function sync_api_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 sync_api_row_record_id(string $tableName, array $row, string $profileId): string
{
    $explicitId = trim((string)($row['id'] ?? ''));
    if ($explicitId !== '') {
        return $explicitId;
    }
    if ($tableName === 'app_settings') {
        return sync_api_setting_record_id($profileId, (string)($row['setting_key'] ?? ''));
    }
    if ($tableName === 'agenda_notes') {
        return sync_api_setting_record_id($profileId, 'agenda:' . (string)($row['note_key'] ?? ''));
    }
    if ($tableName === 'user_permissions') {
        return sync_api_setting_record_id((string)($row['user_id'] ?? ''), 'perm:' . (string)($row['permission_code'] ?? ''));
    }
    if ($tableName === 'user_access_scopes') {
        return sync_api_setting_record_id(
            (string)($row['user_id'] ?? ''),
            'scope:' . (string)($row['scope_type'] ?? '') . ':' . (string)($row['target_id'] ?? '')
        );
    }
    return sync_api_setting_record_id($profileId, $tableName . ':' . md5(json_encode($row, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?: '{}'));
}

function sync_api_insert_global_event(
    PDO $db,
    string $profileId,
    string $tableName,
    string $actionType,
    string $recordId,
    ?array $beforeRow,
    ?array $afterRow,
    array $meta = [],
    string $transactionUuid = '',
    string $deviceId = ''
): int {
    sync_api_ensure_global_events_table($db);
    $tx = trim($transactionUuid);
    if ($tx === '') {
        $tx = sync_api_uuid4();
    }
    $payloadJson = json_encode(
        [
            'profile_id' => $profileId,
            'before' => $beforeRow,
            'after' => $afterRow,
            'meta' => $meta,
        ],
        JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
    );
    if ($payloadJson === false) {
        $payloadJson = '{}';
    }
    $db->prepare(
        'INSERT INTO global_sync_events
         (transaction_uuid, device_id, profile_id, table_name, action_type, record_id, payload_json, created_at)
         VALUES (:tx, :dev, :pid, :tbl, :act, :rid, :payload, NOW())'
    )->execute([
        ':tx' => $tx,
        ':dev' => $deviceId,
        ':pid' => $profileId,
        ':tbl' => $tableName,
        ':act' => strtoupper(trim($actionType)),
        ':rid' => $recordId,
        ':payload' => $payloadJson,
    ]);
    return (int)$db->lastInsertId();
}

function sync_api_profile_table_names(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) {
        $tableName = trim((string)($row['TABLE_NAME'] ?? ''));
        if ($tableName === '' || !sync_api_is_safe_table_name($tableName)) {
            continue;
        }
        if (in_array($tableName, ['profiles', 'local_sync_queue', 'sync_state', 'sync_profile_state', 'conflict_logs', 'global_sync_events', 'deleted_profiles', 'profile_sync_jobs'], true)) {
            continue;
        }
        $tables[] = $tableName;
    }
    return $tables;
}

function sync_api_profile_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 $tableName) {
        $name = trim((string)$tableName);
        if ($name !== '') {
            $remaining[$name] = 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 sync_api_collect_profile_rows(PDO $tenant, string $profileId): array
{
    $result = [];
    foreach (sync_api_profile_table_insert_order(sync_api_profile_table_names($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[] = sync_api_prepare_row_data($tenant, $tableName, $row, $profileId, sync_api_row_record_id($tableName, $row, $profileId));
            }
        }
        $result[$tableName] = $rows;
    }
    return $result;
}

function sync_api_related_user_rows(PDO $tenant, string $profileId): array
{
    $userRows = $tenant->prepare('SELECT id FROM users WHERE profile_id = :pid');
    $userRows->execute([':pid' => $profileId]);
    $userIds = [];
    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), '?'));
    $result = [
        'user_permissions' => [],
        'user_access_scopes' => [],
    ];
    foreach (array_keys($result) 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)) {
                $result[$tableName][] = sync_api_prepare_row_data(
                    $tenant,
                    $tableName,
                    $row,
                    $profileId,
                    sync_api_row_record_id($tableName, $row, $profileId)
                );
            }
        }
    }
    return $result;
}

function sync_api_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]);
    $profileRow = $profileSt->fetch();
    if (!$profileRow || !is_array($profileRow)) {
        sync_api_json_error(404, 'NOT_FOUND', 'Profile not found in tenant database.');
    }
    return [
        'schema_version' => 'profile-sync-v1',
        'profile' => sync_api_prepare_row_data($tenant, 'profiles', $profileRow, $profileId, $profileId),
        'tables' => sync_api_collect_profile_rows($tenant, $profileId),
        'related' => sync_api_related_user_rows($tenant, $profileId),
    ];
}

function sync_api_apply_profile_snapshot(
    PDO $tenant,
    array $snapshot,
    string $deviceId = '',
    string $transactionUuid = ''
): array
{
    $profileRow = is_array($snapshot['profile'] ?? null) ? $snapshot['profile'] : [];
    $profileId = trim((string)($profileRow['id'] ?? ''));
    if ($profileId === '') {
        sync_api_json_error(400, 'INVALID_REQUEST', 'Snapshot profile id is required.');
    }
    if (sync_api_deleted_profile_row($tenant, $profileId)) {
        sync_api_json_error(409, 'TOMBSTONED_PROFILE', 'Profile was deleted and cannot be restored automatically.');
    }
    $tables = is_array($snapshot['tables'] ?? null) ? $snapshot['tables'] : [];
    $related = is_array($snapshot['related'] ?? null) ? $snapshot['related'] : [];
    $tx = trim($transactionUuid);
    if ($tx === '') {
        $tx = sync_api_uuid4();
    }
    $existingTx = sync_api_tx_event_summary($tenant, $tx);
    if ((int)$existingTx['event_count'] > 0) {
        return [
            'profile_id' => $profileId,
            'max_event_id' => (int)$existingTx['max_event_id'],
            'event_count' => (int)$existingTx['event_count'],
            'duplicate' => true,
        ];
    }
    $profileExists = sync_api_profile_exists($tenant, $profileId);
    $maxEventId = 0;
    $eventCount = 0;

    // DDL (Create Table) komutlari Transaction'i sonlandirdigi icin disari aldik:
    sync_api_ensure_global_events_table($tenant);
    sync_api_ensure_deleted_profiles_table($tenant);

    $tenant->beginTransaction();
    try {
        $preparedProfile = sync_api_prepare_row_data($tenant, 'profiles', $profileRow, $profileId, $profileId);
        sync_api_upsert_row($tenant, 'profiles', $preparedProfile);
        $maxEventId = sync_api_insert_global_event(
            $tenant,
            $profileId,
            'profiles',
            $profileExists ? 'UPDATE' : 'ADD',
            $profileId,
            null,
            $preparedProfile,
            ['source' => 'sync.profiles.import'],
            $tx,
            $deviceId
        );
        $eventCount++;

        foreach (sync_api_profile_table_insert_order(array_keys($tables)) as $tableName) {
            $rows = $tables[$tableName] ?? [];
            if (!sync_api_is_safe_table_name($tableName) || !is_array($rows)) {
                continue;
            }
            foreach ($rows as $row) {
                if (!is_array($row)) {
                    continue;
                }
                $prepared = sync_api_prepare_row_data(
                    $tenant,
                    $tableName,
                    $row,
                    $profileId,
                    sync_api_row_record_id($tableName, $row, $profileId)
                );
                if (!$prepared) {
                    continue;
                }
                sync_api_upsert_row($tenant, $tableName, $prepared);
                $maxEventId = sync_api_insert_global_event(
                    $tenant,
                    $profileId,
                    $tableName,
                    'ADD',
                    sync_api_row_record_id($tableName, $prepared, $profileId),
                    null,
                    $prepared,
                    ['source' => 'sync.profiles.import'],
                    $tx,
                    $deviceId
                );
                $eventCount++;
            }
        }

        foreach (['user_permissions', 'user_access_scopes'] as $tableName) {
            $rows = $related[$tableName] ?? [];
            if (!is_array($rows)) {
                continue;
            }
            foreach ($rows as $row) {
                if (!is_array($row)) {
                    continue;
                }
                $prepared = sync_api_prepare_row_data(
                    $tenant,
                    $tableName,
                    $row,
                    $profileId,
                    sync_api_row_record_id($tableName, $row, $profileId)
                );
                if (!$prepared) {
                    continue;
                }
                sync_api_upsert_row($tenant, $tableName, $prepared);
                $maxEventId = sync_api_insert_global_event(
                    $tenant,
                    $profileId,
                    $tableName,
                    'ADD',
                    sync_api_row_record_id($tableName, $prepared, $profileId),
                    null,
                    $prepared,
                    ['source' => 'sync.profiles.import'],
                    $tx,
                    $deviceId
                );
                $eventCount++;
            }
        }

        $tenant->commit();
    } catch (Throwable $e) {
        try {
            if ($tenant->inTransaction()) {
                $tenant->rollBack();
            }
        } catch (Throwable $ignore) {
            // Hata maskelenmesin diye rollback hatasini yutuyoruz.
        }
        sync_api_json_error(500, 'IMPORT_FAILED', 'Profile snapshot import failed: ' . $e->getMessage());
    }

    return [
        'profile_id' => $profileId,
        'max_event_id' => $maxEventId,
        'event_count' => $eventCount,
    ];
}

function sync_api_read_json_body(): array
{
    $raw = file_get_contents('php://input');
    if (!is_string($raw) || trim($raw) === '') {
        return [];
    }
    $decoded = json_decode($raw, true);
    return is_array($decoded) ? $decoded : [];
}



