<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Storage;

class CreatorScheduledPost extends Model
{
    use HasFactory;

    protected $fillable = [
        'user_id',
        'social_account_id',
        'caption',
        'media_refs',
        'publish_time',
        'status',
        'timezone',
        'retry_policy',
        'idempotency_key',
        'campaign_id',
        'post_visibility',
        'recurrence',
        'remote_post_id',
        'provider_response',
        'error_message',
        'attempt_count',
        'last_attempt_at',
        'next_retry_at',
        'auto_reply_enabled',
        'auto_reply_template',
    ];

    protected $casts = [
        'media_refs' => 'array',
        'publish_time' => 'datetime',
        'recurrence' => 'array',
        'attempt_count' => 'integer',
        'last_attempt_at' => 'datetime',
        'next_retry_at' => 'datetime',
        'auto_reply_enabled' => 'boolean',
        'created_at' => 'datetime',
        'updated_at' => 'datetime',
    ];

    /**
     * Get the user that owns the scheduled post.
     */
    public function user()
    {
        return $this->belongsTo(User::class);
    }

    /**
     * Get the social account for this scheduled post.
     */
    public function socialAccount()
    {
        return $this->belongsTo(SocialAccount::class);
    }

    /**
     * Get the campaign this scheduled post belongs to.
     */
    public function campaign()
    {
        return $this->belongsTo(Campaign::class);
    }

    /**
     * Get the auto reply template for this scheduled post.
     */
    public function autoReplyTemplate()
    {
        return $this->belongsTo(AutoReplyTemplate::class);
    }

    /**
     * Check if the scheduled post is due for publishing.
     */
    public function isDue(): bool
    {
        return $this->publish_time && $this->publish_time->isPast() && $this->status === 'scheduled';
    }

    /**
     * Mark the scheduled post as scheduled.
     */
    public function markScheduled(): void
    {
        $this->status = 'scheduled';
        $this->save();
    }

    /**
     * Mark the scheduled post as publishing.
     */
    public function markPublishing(): void
    {
        $this->status = 'publishing';
        $this->save();
    }

    /**
     * Mark the scheduled post as posted.
     */
    public function markPosted(string $remotePostId, array $providerResponse): void
    {
        $this->status = 'posted';
        $this->remote_post_id = $remotePostId;
        $this->provider_response = $providerResponse;
        $this->save();
    }

    /**
     * Mark the scheduled post as failed.
     */
    public function markFailed(string $message): void
    {
        $this->status = 'failed';
        $this->error_message = $message;
        $this->save();
    }

    /**
     * Mark the scheduled post as cancelled.
     */
    public function markCancelled(): void
    {
        $this->status = 'cancelled';
        $this->save();
    }

    /**
     * Get signed URLs for all media references.
     */
    public function getSignedMediaUrls(): array
    {
        $urls = [];
        
        if (!is_array($this->media_refs)) {
            return $urls;
        }
        
        foreach ($this->media_refs as $ref) {
            if (isset($ref['url'])) {
                $urls[] = $ref['url'];
            } elseif (isset($ref['path']) && Storage::disk('public')->exists($ref['path'])) {
                $expireMinutes = config('scheduled_posts.signed_url_expire', 300);
                $urls[] = Storage::disk('public')->temporaryUrl(
                    $ref['path'],
                    now()->addMinutes($expireMinutes)
                );
            }
        }
        
        return $urls;
    }

    /**
     * Get the decrypted media references.
     */
    public function getDecryptedMediaRefs(): array
    {
        if (!is_array($this->media_refs)) {
            return [];
        }

        $decryptedRefs = [];
        foreach ($this->media_refs as $ref) {
            if (isset($ref['encrypted']) && $ref['encrypted']) {
                try {
                    $decryptedRefs[] = [
                        'url' => Crypt::decryptString($ref['url']),
                        'type' => $ref['type'] ?? 'image',
                    ];
                } catch (\Exception $e) {
                    // Log error and skip this ref
                    \Log::warning('Failed to decrypt media reference: ' . $e->getMessage());
                }
            } else {
                $decryptedRefs[] = $ref;
            }
        }

        return $decryptedRefs;
    }

    /**
     * Calculate the next retry time based on retry policy.
     */
    public function calculateNextRetryTime(): ?\DateTime
    {
        if (!$this->retry_policy || !is_array($this->retry_policy)) {
            return null;
        }

        $policy = $this->retry_policy;
        $attempt = $this->attempt_count;

        // Check if we've exceeded max attempts
        if (isset($policy['max_attempts']) && $attempt >= $policy['max_attempts']) {
            return null;
        }

        // Calculate exponential backoff
        if (isset($policy['backoff']) && $policy['backoff'] === 'exponential') {
            $baseDelay = $policy['base_delay'] ?? 60; // Default to 1 minute
            $maxDelay = $policy['max_delay'] ?? 86400; // Default to 1 day
            $multiplier = $policy['multiplier'] ?? 2;
            
            $delay = min($baseDelay * pow($multiplier, $attempt - 1), $maxDelay);
            return now()->addSeconds($delay);
        }

        // Fixed delay
        if (isset($policy['delay'])) {
            return now()->addSeconds($policy['delay']);
        }

        return null;
    }

    /**
     * Encrypt media references before saving.
     */
    public function setMediaRefsAttribute($value)
    {
        if (is_array($value)) {
            $encryptedRefs = [];
            foreach ($value as $ref) {
                if (isset($ref['url']) && filter_var($ref['url'], FILTER_VALIDATE_URL)) {
                    // Encrypt the URL
                    $encryptedRefs[] = [
                        'url' => Crypt::encryptString($ref['url']),
                        'type' => $ref['type'] ?? 'image',
                        'encrypted' => true,
                    ];
                } else {
                    $encryptedRefs[] = $ref;
                }
            }
            $this->attributes['media_refs'] = json_encode($encryptedRefs);
        } else {
            $this->attributes['media_refs'] = $value;
        }
    }
}