<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

/**
 * VotingProcedure Model
 *
 * Represents a voting procedure for leadership elections with
 * description, voting method, rules, and version tracking.
 *
 * @property int $id
 * @property int $organization_id
 * @property string $name
 * @property string $description
 * @property string $voting_method
 * @property string $rules
 * @property int $version
 * @property \Illuminate\Support\Carbon|null $created_at
 * @property \Illuminate\Support\Carbon|null $updated_at
 * @property \Illuminate\Support\Carbon|null $deleted_at
 */
class VotingProcedure extends Model
{
    use HasFactory, SoftDeletes;

    /**
     * The available voting methods with human-readable labels.
     */
    const VOTING_METHODS = [
        'majority'                  => 'Simple Majority (>50%)',
        'plurality'                 => 'Plurality (Most Votes)',
        'supermajority_two_thirds'  => 'Supermajority (2/3)',
        'supermajority_three_fourths' => 'Supermajority (3/4)',
        'unanimous'                 => 'Unanimous (100%)',
        'ranked_choice'             => 'Ranked Choice Voting',
        'approval'                  => 'Approval Voting',
        'borda_count'               => 'Borda Count',
    ];

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'organization_id',
        'name',
        'description',
        'voting_method',
        'rules',
        'version',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'version' => 'integer',
        'created_at' => 'datetime',
        'updated_at' => 'datetime',
    ];

    /**
     * The "booted" method of the model.
     */
    protected static function booted(): void
    {
        static::creating(function (VotingProcedure $procedure) {
            // Set default version if not set
            if (!$procedure->version) {
                $procedure->version = 1;
            }
        });

        static::updating(function (VotingProcedure $procedure) {
            // Increment version on update
            if ($procedure->isDirty(['name', 'description', 'voting_method', 'rules'])) {
                $procedure->version++;
            }
        });
    }

    /**
     * Get the organization that owns the voting procedure.
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function organization()
    {
        return $this->belongsTo(Organization::class);
    }

    /**
     * Get the election cycles that use this voting procedure.
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
     */
    public function electionCycles()
    {
        return $this->belongsToMany(ElectionCycle::class, 'election_cycle_voting_procedure')
            ->withTimestamps();
    }

    /**
     * Sync this voting procedure with election cycles.
     *
     * @param array $electionCycleIds
     * @return void
     */
    public function syncElectionCycles(array $electionCycleIds): void
    {
        $this->electionCycles()->sync($electionCycleIds);
    }

    /**
     * Get the version history for this voting procedure.
     * Note: This returns all versions with the same name in the same organization.
     *
     * @return \Illuminate\Database\Eloquent\Collection
     */
    public function getVersionHistory()
    {
        return static::withTrashed()
            ->where('organization_id', $this->organization_id)
            ->where('name', $this->name)
            ->orderBy('version', 'desc')
            ->get();
    }

    /**
     * Check if this procedure is associated with any active election cycles.
     *
     * @return bool
     */
    public function hasActiveElectionCycles(): bool
    {
        return $this->electionCycles()->active()->exists();
    }

    /**
     * Get the human-readable label for the voting method.
     *
     * @return string
     */
    public function getVotingMethodLabelAttribute(): string
    {
        return self::VOTING_METHODS[$this->voting_method] ?? ucwords(str_replace('_', ' ', $this->voting_method));
    }

    /**
     * Get the associated election cycles count.
     *
     * @return int
     */
    public function getElectionCyclesCount(): int
    {
        return $this->electionCycles()->count();
    }

    /**
     * Scope to get voting procedures for a specific organization.
     *
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @param int $organizationId
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeForOrganization($query, int $organizationId)
    {
        return $query->where('organization_id', $organizationId);
    }

    /**
     * Scope to get the latest version of each voting procedure.
     *
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeLatestVersions($query)
    {
        return $query->orderBy('version', 'desc');
    }
}
