<?php

namespace App\Libs\Database\Eloquent\Traits;

use Illuminate\Support\Facades\DB;

/**
 * Trait to provide safe sync functionality that preserves hidden items.
 *
 * When a global scope filters out items that the user cannot see,
 * a normal sync() would delete those hidden items. This trait provides
 * a safeSync() method that preserves hidden items while syncing visible ones.
 */
trait SafeSyncTrait
{
    /**
     * Perform a safe sync on a BelongsToMany relationship.
     * This preserves related items that are hidden by global scopes.
     *
     * @param string $relationName The name of the relationship method
     * @param array $visibleIds The IDs that the user can see and wants to sync
     * @param bool $withPosition Whether to include position data
     * @return array The result of the sync operation
     */
    public function safeSync(string $relationName, array $visibleIds, bool $withPosition = false): array
    {
        $relation = $this->$relationName();
        $relatedModel = $relation->getRelated();
        $pivotTable = $relation->getTable();
        $foreignPivotKey = $relation->getForeignPivotKeyName();
        $relatedPivotKey = $relation->getRelatedPivotKeyName();

        // Get ALL current related IDs directly from pivot table (bypassing scopes)
        $allCurrentIds = DB::table($pivotTable)
            ->where($foreignPivotKey, $this->id)
            ->pluck($relatedPivotKey)
            ->toArray();

        // Get the IDs that the user CAN see (with scopes applied)
        $visibleCurrentIds = $relation->pluck($relatedModel->getTable() . '.id')->toArray();

        // Calculate hidden IDs (items the user cannot see due to scopes)
        $hiddenIds = array_diff($allCurrentIds, $visibleCurrentIds);

        // Merge hidden IDs with the new visible IDs to preserve hidden items
        if ($withPosition) {
            // Build sync data with positions for visible items
            $syncData = [];
            $position = 1;
            foreach ($visibleIds as $id) {
                $syncData[$id] = ['position' => $position];
                $position++;
            }

            // Add hidden items with their current positions (or high positions to keep them at the end)
            $hiddenPositions = DB::table($pivotTable)
                ->where($foreignPivotKey, $this->id)
                ->whereIn($relatedPivotKey, $hiddenIds)
                ->pluck('position', $relatedPivotKey)
                ->toArray();

            foreach ($hiddenIds as $hiddenId) {
                // Keep original position or assign a high position
                $syncData[$hiddenId] = ['position' => $hiddenPositions[$hiddenId] ?? ($position++)];
            }

            return $relation->sync($syncData);
        } else {
            // Simple merge without positions
            $allIds = array_unique(array_merge($visibleIds, $hiddenIds));
            return $relation->sync($allIds);
        }
    }

    /**
     * Check if the related model of a relationship has a specific global scope.
     *
     * @param string $relationName The name of the relationship method
     * @param string $scopeName The name of the global scope to check
     * @return bool
     */
    public function relationHasGlobalScope(string $relationName, string $scopeName = 'visibleByBackendUser'): bool
    {
        $relation = $this->$relationName();
        $relatedModel = $relation->getRelated();

        return $relatedModel->hasGlobalScope($scopeName);
    }
}
