<?php

namespace App\Models;

use App\Services\Platforms\Auth\TokenRefresherFactory;
use Database\Factories\PlatformConnectionFactory;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Facades\Log;

class PlatformConnection extends Model
{
    /** @use HasFactory<PlatformConnectionFactory> */
    use HasFactory;

    protected $fillable = [
        'user_id',
        'platform',
        'name',
        'account_id',
        'access_token',
        'refresh_token',
        'token_type',
        'expires_at',
        'refresh_expires_at',
        'last_refreshed_at',
        'scopes',
        'platform_data',
        'is_active',
        'last_error',
    ];

    protected $hidden = [
        'access_token',
        'refresh_token',
        'platform_data',
    ];

    protected function casts(): array
    {
        return [
            'access_token' => 'encrypted',
            'refresh_token' => 'encrypted',
            'platform_data' => 'encrypted:array',
            'scopes' => 'array',
            'expires_at' => 'datetime',
            'refresh_expires_at' => 'datetime',
            'last_refreshed_at' => 'datetime',
            'is_active' => 'boolean',
        ];
    }

    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }

    /**
     * Scope to active connections only.
     *
     * @param  Builder<PlatformConnection>  $query
     */
    public function scopeActive(Builder $query): Builder
    {
        return $query->where('is_active', true);
    }

    /**
     * Scope to a specific platform.
     *
     * @param  Builder<PlatformConnection>  $query
     */
    public function scopeForPlatform(Builder $query, string $platform): Builder
    {
        return $query->where('platform', strtolower(trim($platform)));
    }

    /**
     * Scope to connections whose access tokens are expiring within a number of days.
     *
     * @param  Builder<PlatformConnection>  $query
     */
    public function scopeExpiringSoon(Builder $query, int $days = 7): Builder
    {
        return $query->active()
            ->where(function (Builder $q): void {
                $q->whereNotNull('refresh_token')
                    ->orWhere('platform', 'meta');
            })
            ->where(function (Builder $subQuery) use ($days): void {
                $subQuery->whereNull('expires_at')
                    ->orWhere('expires_at', '<=', now()->addDays($days));
            });
    }

    /**
     * The primary shared connection for automated campaign flows.
     */
    public static function shared(string $platform): ?self
    {
        try {
            return self::query()
                ->forPlatform($platform)
                ->active()
                ->whereNull('user_id')
                ->latest('id')
                ->first()
                ?? self::query()
                    ->forPlatform($platform)
                    ->active()
                    ->latest('id')
                    ->first();
        } catch (\Throwable $e) {
            Log::warning("Failed to query shared PlatformConnection for '{$platform}': {$e->getMessage()}", [
                'platform' => $platform,
                'exception' => $e,
            ]);

            return null;
        }
    }

    /**
     * Whether the access token is already past expiry.
     */
    public function isExpired(): bool
    {
        return $this->expires_at !== null && $this->expires_at->isPast();
    }

    /**
     * Whether this token needs a refresh (either expired or within $daysThreshold days).
     */
    public function needsRefresh(int $daysThreshold = 7): bool
    {
        if (blank($this->refresh_token) && $this->platform !== 'meta') {
            return false;
        }

        if ($this->expires_at === null) {
            return true;
        }

        return $this->expires_at->lte(now()->addDays($daysThreshold));
    }

    /**
     * Refresh the access token using the platform's refresher.
     */
    public function refreshAccessToken(): self
    {
        return app(TokenRefresherFactory::class)->for($this)->refresh($this);
    }
}
