<?php

namespace App\Models;

use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Facades\Crypt;

/**
 * @property int $id
 * @property string $name
 * @property string|null $description
 * @property string|null $module
 * @property string $hostname
 * @property string $ip_address
 * @property string $username
 * @property string $password
 * @property int $port
 * @property string $status
 * @property int|null $maximum_hosts
 * @property int $current_hosts
 * @property \Illuminate\Support\Carbon|null $created_at
 * @property \Illuminate\Support\Carbon|null $updated_at
 *
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Server newModelQuery()
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Server newQuery()
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Server query()
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Server whereCreatedAt($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Server whereCurrentHosts($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Server whereDescription($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Server whereHostname($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Server whereId($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Server whereIpAddress($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Server whereMaximumHosts($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Server whereModule($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Server whereName($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Server wherePassword($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Server wherePort($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Server whereStatus($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Server whereUpdatedAt($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Server whereUsername($value)
 *
 * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ClientProduct> $clientProducts
 * @property-read int|null $client_products_count
 * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ServerSetting> $settings
 * @property-read int|null $settings_count
 * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ServerGroup> $groups
 * @property-read int|null $groups_count
 *
 * @mixin \Eloquent
 */
class Server extends Model
{
    use HasFactory;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'name',
        'description',
        'module',
        'hostname',
        'ip_address',
        'username',
        'password',
        'port',
        'status',
        'maximum_hosts',
        'current_hosts',
        'stock',
        // PRC-817 — fiziksel asset envanteri + lokasyon
        'server_type',
        'serial_number',
        'asset_tag',
        'manufacturer',
        'model_name',
        'specs',
        'purchase_date',
        'warranty_expiry',
        'asset_status',
        'datacenter_id',
        'rack_id',
        'u_position',
        'u_size',
        'client_product_id',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'port' => 'integer',
        'maximum_hosts' => 'integer',
        'current_hosts' => 'integer',
        'stock' => 'integer',
        // PRC-817
        'specs' => 'array',
        'purchase_date' => 'date',
        'warranty_expiry' => 'date',
        'u_position' => 'integer',
        'u_size' => 'integer',
    ];

    /**
     * PRC-169: hide password from toArray()/toJson() — the accessor returns
     * plaintext (needed for Blade form pre-fill and module provisioning),
     * but the value must never leak into JSON API responses or logs.
     */
    protected $hidden = [
        'password',
    ];

    /**
     * PRC-169: server.password is encrypted at-rest. Accessor falls back to
     * the raw value if decryption fails so legacy plaintext rows continue
     * to work during the retroactive encrypt migration. Mutator always
     * encrypts on write — every new save lands as ciphertext.
     */
    public function getPasswordAttribute(?string $value): ?string
    {
        if ($value === null || $value === '') {
            return $value;
        }
        try {
            return Crypt::decryptString($value);
        } catch (DecryptException $e) {
            return $value;
        }
    }

    public function setPasswordAttribute(?string $value): void
    {
        if ($value === null || $value === '') {
            $this->attributes['password'] = $value;
            return;
        }
        $this->attributes['password'] = Crypt::encryptString($value);
    }

    /**
     * Get the client products for the server.
     */
    public function clientProducts(): HasMany
    {
        return $this->hasMany(ClientProduct::class);
    }

    /**
     * Get the server settings.
     */
    public function settings(): HasMany
    {
        return $this->hasMany(ServerSetting::class);
    }

    /**
     * Get the server groups.
     */
    public function groups(): BelongsToMany
    {
        return $this->belongsToMany(ServerGroup::class, 'server_group_relations', 'server_id', 'group_id')
            ->withTimestamps();
    }

    // ---------------------------------------------------------------------
    // PRC-817 — Fiziksel asset envanteri + lokasyon
    // ---------------------------------------------------------------------

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

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

    /** Şu an bu asset'e bağlı satılan hizmet (müşteri geçmişinin ucu). */
    public function clientProduct(): BelongsTo
    {
        return $this->belongsTo(ClientProduct::class);
    }

    /**
     * DUAL-PURPOSE GUARD (PRC-817) — provisioning host seçimi YALNIZ bu scope'u kullanmalı.
     *
     * Yalnız `provisioning` veya `both` tipli sunucular provisioning host'u olabilir;
     * `physical`-only asset'ler provisioning listesine ÇIKMAZ. Mevcut kayıtlar migration
     * default'u ile `provisioning` → geriye-uyumlu (regresyon yok).
     */
    public function scopeProvisionable($query)
    {
        return $query->whereIn('server_type', ['provisioning', 'both']);
    }

    /** Fiziksel envanterde görünen (asset) sunucular. */
    public function scopePhysical($query)
    {
        return $query->whereIn('server_type', ['physical', 'both']);
    }
}
