<?php

namespace App\Models;

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

/**
 * ElectionCycle Model
 *
 * Represents an election cycle for leadership positions with duration,
 * start/end dates, status, and applicable positions.
 *
 * @property int $id
 * @property int $organization_id
 * @property string $name
 * @property float $duration_years
 * @property \Illuminate\Support\Carbon $start_date
 * @property \Illuminate\Support\Carbon $end_date
 * @property string $status
 * @property array $applicable_positions
 * @property \Illuminate\Support\Carbon|null $created_at
 * @property \Illuminate\Support\Carbon|null $updated_at
 * @property \Illuminate\Support\Carbon|null $deleted_at
 */
class ElectionCycle extends Model
{
    use HasFactory, SoftDeletes;

    /**
     * The possible statuses for an election cycle.
     */
    const STATUS_ACTIVE = 'active';
    const STATUS_COMPLETED = 'completed';
    const STATUS_CANCELLED = 'cancelled';

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'organization_id',
        'name',
        'duration_years',
        'start_date',
        'end_date',
        'status',
        'applicable_positions',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'start_date' => 'date',
        'end_date' => 'date',
        'duration_years' => 'decimal:1',
        'applicable_positions' => 'array',
        'created_at' => 'datetime',
        'updated_at' => 'datetime',
    ];

    /**
     * The "booted" method of the model.
     */
    protected static function booted(): void
    {
        static::creating(function (ElectionCycle $cycle) {
            // Automatically calculate end_date if not set
            if (!$cycle->end_date && $cycle->start_date && $cycle->duration_years) {
                $cycle->end_date = $cycle->calculateEndDate();
            }
        });
    }

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

    /**
     * Get the voting procedures associated with this election cycle.
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
     */
    public function votingProcedures()
    {
        return $this->belongsToMany(VotingProcedure::class, 'election_cycle_voting_procedure')
            ->withTimestamps();
    }

    /**
     * Get the leadership positions that this election cycle applies to.
     *
     * @return \Illuminate\Database\Eloquent\Collection
     */
    public function getApplicablePositions()
    {
        if (empty($this->applicable_positions)) {
            return collect();
        }

        return LeadershipPosition::whereIn('id', $this->applicable_positions)->get();
    }

    /**
     * Calculate the end date based on start date and duration.
     *
     * @return \Illuminate\Support\Carbon
     */
    public function calculateEndDate()
    {
        $years = floor($this->duration_years);
        $months = ($this->duration_years - $years) * 12;

        return $this->start_date->copy()
            ->addYears((int) $years)
            ->addMonths((int) $months);
    }

    /**
     * Mark this election cycle as completed.
     *
     * @return bool
     */
    public function complete(): bool
    {
        $this->status = self::STATUS_COMPLETED;
        $this->end_date = now();
        return $this->save();
    }

    /**
     * Mark this election cycle as cancelled.
     *
     * @return bool
     */
    public function cancel(): bool
    {
        $this->status = self::STATUS_CANCELLED;
        return $this->save();
    }

    /**
     * Check if this election cycle is active.
     *
     * @return bool
     */
    public function isActive(): bool
    {
        return $this->status === self::STATUS_ACTIVE;
    }

    /**
     * Check if this election cycle is completed.
     *
     * @return bool
     */
    public function isCompleted(): bool
    {
        return $this->status === self::STATUS_COMPLETED;
    }

    /**
     * Check if this election cycle is cancelled.
     *
     * @return bool
     */
    public function isCancelled(): bool
    {
        return $this->status === self::STATUS_CANCELLED;
    }

    /**
     * Get the days until the next election.
     *
     * @return int|null
     */
    public function getDaysUntilNextElection(): ?int
    {
        if (!$this->isActive()) {
            return null;
        }

        return now()->diffInDays($this->end_date, false);
    }

    /**
     * Get the status label.
     *
     * @return string
     */
    public function getStatusLabelAttribute(): string
    {
        return ucfirst($this->status);
    }

    /**
     * Scope to get only active election cycles.
     *
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeActive($query)
    {
        return $query->where('status', self::STATUS_ACTIVE);
    }

    /**
     * Scope to get only completed election cycles.
     *
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeCompleted($query)
    {
        return $query->where('status', self::STATUS_COMPLETED);
    }

    /**
     * Scope to get only cancelled election cycles.
     *
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeCancelled($query)
    {
        return $query->where('status', self::STATUS_CANCELLED);
    }

    /**
     * Scope to get election cycles 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);
    }
}
