<?php

namespace App\Models;

use App\Enums\AccountStatusEnum;
use App\Enums\PlatformEnum;
use Database\Factories\PlatformAccountFactory;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Crypt;

/**
 * @property int $id
 * @property PlatformEnum $platform
 * @property string $instance_url
 * @property string $username
 * @property string $password
 * @property array<string, mixed> $settings
 * @property bool $is_active
 * @property Carbon|null $last_tested_at
 * @property AccountStatusEnum $status
 * @property int $consecutive_failures
 * @property Carbon $created_at
 * @property Carbon $updated_at
 * @property Collection<int, PlatformChannel> $activeChannels
 *
 * @method static where(string $string, PlatformEnum $platform)
 * @method static orderBy(string $string)
 * @method static create(array<string, mixed> $validated)
 */
class PlatformAccount extends Model
{
    /** @use HasFactory<PlatformAccountFactory> */
    use HasFactory;

    protected $fillable = [
        'platform',
        'instance_url',
        'username',
        'password',
        'settings',
        'is_active',
        'last_tested_at',
        'status',
        'consecutive_failures',
    ];

    protected $casts = [
        'platform' => PlatformEnum::class,
        'status' => AccountStatusEnum::class,
        'settings' => 'array',
        'is_active' => 'boolean',
        'last_tested_at' => 'datetime',
    ];

    // Encrypt password when storing
    /**
     * @return Attribute<string|null, string|null>
     */
    protected function password(): Attribute
    {
        return Attribute::make(
            get: function ($value, array $attributes) {
                // Return null if the raw value is null
                if (is_null($value)) {
                    return null;
                }

                // Return empty string if value is empty
                if (empty($value)) {
                    return '';
                }

                try {
                    return Crypt::decryptString($value);
                } catch (\Exception $e) {
                    // If decryption fails, return null to be safe
                    return null;
                }
            },
            set: function ($value) {
                // Store null if null is passed
                if (is_null($value)) {
                    return null;
                }

                // Store empty string as null
                if (empty($value)) {
                    return null;
                }

                return Crypt::encryptString($value);
            },
        )->withoutObjectCaching();
    }

    // Get the active accounts for a platform (returns collection)
    /**
     * @return Collection<int, PlatformAccount>
     */
    public static function getActive(PlatformEnum $platform): Collection
    {
        return static::where('platform', $platform)
            ->where('is_active', true)
            ->get();
    }

    // Set this account as active (deactivates others for same platform)
    public function setAsActive(): void
    {
        // Deactivate other accounts for this platform
        static::where('platform', $this->platform)
            ->where('id', '!=', $this->id)
            ->update(['is_active' => false]);

        // Activate this account
        $this->update(['is_active' => true]);
    }

    /**
     * @return BelongsToMany<PlatformChannel, $this>
     */
    public function channels(): BelongsToMany
    {
        return $this->belongsToMany(PlatformChannel::class, 'platform_account_channels')
            ->withPivot(['is_active', 'priority'])
            ->withTimestamps();
    }

    /**
     * @return BelongsToMany<PlatformChannel, $this>
     */
    public function activeChannels(): BelongsToMany
    {
        return $this->channels()
            ->wherePivot('is_active', true)
            ->orderByPivot('priority', 'desc');
    }

    public const FAILURES_BEFORE_UNHEALTHY = 3;

    public function recordCredentialCheckPassed(): void
    {
        $this->update([
            'status' => AccountStatusEnum::HEALTHY,
            'consecutive_failures' => 0,
            'last_tested_at' => now(),
        ]);
    }

    public function recordCredentialCheckFailed(): void
    {
        $failures = $this->consecutive_failures + 1;

        $this->update([
            'status' => $failures >= self::FAILURES_BEFORE_UNHEALTHY
                ? AccountStatusEnum::UNHEALTHY
                : $this->status,
            'consecutive_failures' => $failures,
            'last_tested_at' => now(),
        ]);
    }

    public function isUnhealthy(): bool
    {
        return $this->status === AccountStatusEnum::UNHEALTHY;
    }
}
