<?php

namespace App\Models;

use App\Enums\AdType;
use App\Enums\CampaignStatus;
use Database\Factories\AdFactory;
use Illuminate\Database\Eloquent\Builder;
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\Carbon;

/**
 * @property int $id
 * @property string $judul
 * @property string $url
 * @property string $jam_tayang_mulai
 * @property string $jam_tayang_selesai
 * @property int $credit
 * @property int $klik
 * @property int $muncul
 * @property int $user_id
 * @property CampaignStatus $status
 * @property Carbon|null $tgl_aktif
 *
 * @method static Builder|self rotatable(int $kategoriId)
 * @method static Builder|self active()
 */
class Ad extends Model
{
    /** @use HasFactory<AdFactory> */
    use HasFactory;

    protected $fillable = [
        'judul', 'url', 'jam_tayang_mulai', 'jam_tayang_selesai',
        'credit', 'klik', 'muncul', 'user_id', 'status', 'tgl_aktif',
        'ad_type', 'ad_platform_id', 'target_country', 'verification_code', 'verified_at',
        'is_paused',
        'budget_harian', 'klik_hari_ini', 'klik_tanggal', 'rollover_klik', 'pause_reason',
        'muncul_hari_ini', 'muncul_tanggal',
    ];

    protected function casts(): array
    {
        return [
            'credit' => 'integer',
            'klik' => 'integer',
            'muncul' => 'integer',
            'status' => CampaignStatus::class,
            'tgl_aktif' => 'datetime',
            'ad_type' => AdType::class,
            'verified_at' => 'datetime',
            'is_paused' => 'boolean',
            'budget_harian' => 'integer',
            'klik_hari_ini' => 'integer',
            'klik_tanggal' => 'date',
            'rollover_klik' => 'integer',
            'muncul_hari_ini' => 'integer',
            'muncul_tanggal' => 'date',
        ];
    }

    public function kategoris(): BelongsToMany
    {
        return $this->belongsToMany(Kategori::class, 'ad_kategori');
    }

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

    public function adsHistories(): HasMany
    {
        return $this->hasMany(AdsHistory::class);
    }

    public function ipHistories(): HasMany
    {
        return $this->hasMany(IpHistory::class);
    }

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

    /**
     * Apakah campaign sudah terverifikasi (untuk link_website)?
     */
    public function isVerified(): bool
    {
        return $this->verified_at !== null;
    }

    /**
     * Apakah campaign butuh verifikasi situs?
     */
    public function needsVerification(): bool
    {
        return $this->ad_type?->needsVerification() ?? false;
    }

    /**
     * Sisa credit yang belum terpakai (legacy, model lama). Display-only.
     */
    public function sisaCredit(): int
    {
        return max(0, $this->credit - $this->klik);
    }

    /**
     * Apakah iklan masih punya sisa klik? (legacy, model lama).
     */
    public function hasRemainingCredit(): bool
    {
        return $this->klik < $this->credit;
    }

    /**
     * Cap klik efektif hari ini = base cap dari budget harian + rollover kemarin.
     * base cap = intdiv(budget_harian, rate).
     */
    public function effectiveDailyCap(int $rate): int
    {
        return intdiv($this->budget_harian ?? 0, $rate) + $this->rollover_klik;
    }

    /**
     * Jumlah klik tercatat hari ini (0 bila counter belum direset hari ini).
     */
    public function clicksToday(): int
    {
        return $this->klik_tanggal?->isToday() ? $this->klik_hari_ini : 0;
    }

    /**
     * Apakah campaign Aktif tapi sedang di-pause (Stop)? Bisa diedit member.
     */
    public function isPausedActive(): bool
    {
        return $this->status === CampaignStatus::Aktif && $this->is_paused;
    }

    /**
     * Scope iklan yang siap ditayangkan rotator (FR-ROT-02, BR-07):
     * status Aktif, dalam jendela jam tayang, kategori cocok,
     * urut klik_hari_ini ASC (least-clicks harian, rotasi adil). Campaign yang
     * sudah diklik IP hari ini dikecualikan agar rotasi adil (BR-08).
     *
     * Catatan: cap harian (budget) dicegah inline di go() via is_paused — bukan
     * di scope, agar klik konkuren di-serialize oleh lockForUpdate di go().
     *
     * @param  string|null  $ip  IP visitor; bila null, tidak filter riwayat klik.
     */
    public function scopeRotatable(Builder $query, int $kategoriId, ?string $ip = null): Builder
    {
        $now = now()->format('H:i:s');

        return $query
            ->where('status', CampaignStatus::Aktif)
            ->where('is_paused', false)
            ->whereHas('kategoris', fn (Builder $q) => $q->where('kategoris.id', $kategoriId))
            ->whereTime('jam_tayang_mulai', '<=', $now)
            ->whereTime('jam_tayang_selesai', '>=', $now)
            ->when($ip, function (Builder $q) use ($ip): void {
                $q->whereNotIn('id', function ($sub) use ($ip): void {
                    $sub->select('ad_id')
                        ->from('ip_histories')
                        ->where('ip', $ip)
                        ->whereDate('tanggal', now()->toDateString());
                });
            })
            // Rotasi berurutan (round-robin) by id: pointer per kategori memilih
            // index berikutnya, sehingga campaign dilayani urut 1,2,3,4 lalu ulang.
            ->orderBy('id', 'asc');
    }

    public function scopeActive(Builder $query): Builder
    {
        return $query->where('status', CampaignStatus::Aktif);
    }
}
