HEX
Server: LiteSpeed
System: Linux s1049.use1.mysecurecloudhost.com 4.18.0-477.27.2.lve.el8.x86_64 #1 SMP Wed Oct 11 12:32:56 UTC 2023 x86_64
User: xedaptot (3356)
PHP: 8.3.31
Disabled: NONE
Upload Files
File: /home/xedaptot/ai.naniguide.com/app/Model/PlanRemoteMapping.php
<?php

namespace App\Model;

use Illuminate\Database\Eloquent\Model;
use App\Library\Facades\Billing;
use App\Library\Traits\HasUid;
use App\Cashier\DTO\RemotePlanDTO;
use App\Cashier\Contracts\RemoteSubscriptionGatewayInterface;

class PlanRemoteMapping extends Model
{
    use HasUid;

    protected $table = 'plan_remote_mappings';

    protected $fillable = [
        'plan_id',
        'payment_gateway_id',
        'remote_plan_id',
        'remote_plan_name',
        'remote_price',
        'remote_currency',
        'remote_interval_count',
        'remote_interval_unit',
        'remote_trial_days',
        'remote_metadata',
        'last_synced_at',
    ];

    protected $casts = [
        'remote_metadata' => 'array',
        'remote_price' => 'float',
        'remote_trial_days' => 'integer',
        'last_synced_at' => 'datetime',
    ];

    public function plan()
    {
        return $this->belongsTo(Plan::class);
    }

    public function paymentGateway()
    {
        return $this->belongsTo(PaymentGateway::class);
    }

    /**
     * Get all mismatches between local plan and remote plan.
     * Returns an array of mismatch descriptions. Empty = all good.
     */
    public function getMismatches(): array
    {
        $plan = $this->plan;
        $mismatches = [];

        // Price
        if ($this->remote_price !== null && abs($plan->price - $this->remote_price) > 0.01) {
            $mismatches[] = [
                'field' => 'price',
                'local' => $plan->price,
                'remote' => $this->remote_price,
                'message' => "Price mismatch: local {$plan->price} vs remote {$this->remote_price}",
            ];
        }

        // Currency
        $localCurrency = strtoupper($plan->currency->code ?? '');
        $remoteCurrency = strtoupper($this->remote_currency ?? '');
        if ($remoteCurrency && $localCurrency !== $remoteCurrency) {
            $mismatches[] = [
                'field' => 'currency',
                'local' => $localCurrency,
                'remote' => $remoteCurrency,
                'message' => "Currency mismatch: local {$localCurrency} vs remote {$remoteCurrency}",
            ];
        }

        // Interval count
        if ($this->remote_interval_count !== null && (int)$plan->frequency_amount !== (int)$this->remote_interval_count) {
            $mismatches[] = [
                'field' => 'interval_count',
                'local' => $plan->frequency_amount,
                'remote' => $this->remote_interval_count,
                'message' => "Interval count mismatch: local {$plan->frequency_amount} vs remote {$this->remote_interval_count}",
            ];
        }

        // Interval unit
        if ($this->remote_interval_unit && $plan->frequency_unit !== $this->remote_interval_unit) {
            $mismatches[] = [
                'field' => 'interval_unit',
                'local' => $plan->frequency_unit,
                'remote' => $this->remote_interval_unit,
                'message' => "Interval unit mismatch: local {$plan->frequency_unit} vs remote {$this->remote_interval_unit}",
            ];
        }

        // Trial days
        $localTrialDays = 0;
        if ($plan->hasTrial() && $plan->trial_amount) {
            $localTrialDays = match ($plan->trial_unit) {
                'day' => (int) $plan->trial_amount,
                'week' => (int) $plan->trial_amount * 7,
                'month' => (int) $plan->trial_amount * 30,
                'year' => (int) $plan->trial_amount * 365,
                default => 0,
            };
        }
        $remoteTrialDays = (int)($this->remote_trial_days ?? 0);
        if ($remoteTrialDays !== $localTrialDays) {
            $mismatches[] = [
                'field' => 'trial_days',
                'local' => $localTrialDays,
                'remote' => $remoteTrialDays,
                'message' => "Trial days mismatch: local {$localTrialDays} vs remote {$remoteTrialDays}",
            ];
        }

        return $mismatches;
    }

    /**
     * Validate that local plan matches remote plan.
     * Throws exception with ALL mismatches listed (not just the first one).
     *
     * @throws \Exception if any mismatch found
     */
    public function validateMapping(): void
    {
        $mismatches = $this->getMismatches();

        if (!empty($mismatches)) {
            $details = array_map(fn($m) => $m['message'], $mismatches);
            throw new \Exception(
                "Plan mapping mismatch for '{$this->plan->name}' ↔ '{$this->remote_plan_name}' " .
                "(gateway: {$this->paymentGateway->name}): " .
                implode('; ', $details)
            );
        }
    }

    /**
     * Check if local plan is in sync with remote plan data.
     */
    public function isInSync(): bool
    {
        return empty($this->getMismatches());
    }

    /**
     * Sync metadata from remote provider
     */
    public function syncFromRemote(RemotePlanDTO $remotePlan): void
    {
        $this->remote_plan_name = $remotePlan->name;
        $this->remote_price = $remotePlan->price;
        $this->remote_currency = $remotePlan->currency;
        $this->remote_interval_count = $remotePlan->intervalCount;
        $this->remote_interval_unit = $remotePlan->intervalUnit;
        $this->remote_trial_days = $remotePlan->trialDays;
        $this->remote_metadata = $remotePlan->metadata;
        $this->last_synced_at = now();
        $this->save();
    }

    /**
     * Get a comparison summary: local vs remote for logging/display.
     */
    public function getComparisonSummary(): array
    {
        $plan = $this->plan;
        $localTrialDays = ($plan->hasTrial() && $plan->trial_unit === 'day')
            ? (int)$plan->trial_amount : 0;

        return [
            'local_plan' => $plan->name,
            'remote_plan' => $this->remote_plan_name,
            'gateway' => $this->paymentGateway->name,
            'gateway_type' => $this->paymentGateway->type,
            'fields' => [
                'price' => ['local' => $plan->price, 'remote' => $this->remote_price],
                'currency' => ['local' => strtoupper($plan->currency->code ?? ''), 'remote' => strtoupper($this->remote_currency ?? '')],
                'interval_count' => ['local' => $plan->frequency_amount, 'remote' => $this->remote_interval_count],
                'interval_unit' => ['local' => $plan->frequency_unit, 'remote' => $this->remote_interval_unit],
                'trial_days' => ['local' => $localTrialDays, 'remote' => $this->remote_trial_days ?? 0],
            ],
            'mismatches' => $this->getMismatches(),
            'in_sync' => $this->isInSync(),
        ];
    }

    /**
     * Get the service instance for this mapping's gateway
     */
    public function getService(): RemoteSubscriptionGatewayInterface
    {
        $service = Billing::resolveService($this->paymentGateway);

        if (!($service instanceof RemoteSubscriptionGatewayInterface)) {
            throw new \Exception(
                "Gateway '{$this->paymentGateway->name}' does not support remote subscriptions"
            );
        }

        return $service;
    }
}