<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;

class Tagihan extends Model
{
    protected $table = 'tagihan';

    protected $fillable = [
        'siswa_id',
        'tarif_pembayaran_id',
        'kelas_id',
        'nama_kelas_snapshot',
        'nama_siswa_snapshot',
        'tahun_ajaran_id',
        'tahun_ajaran_snapshot',
        'bulan',
        'tahun',
        'jumlah_tagihan',
        'jumlah_terbayar',
        'tarif_lama',
        'status',
        'tanggal_jatuh_tempo',
    ];

    /**
     * Prevent changing the academic year of an existing tagihan.
     * This ensures historical data remains tied to the year it was created.
     */
    public function setTahunAjaranIdAttribute($value)
    {
        // If the model already exists and the attribute is set, ignore updates.
        if ($this->exists && $this->getOriginal('tahun_ajaran_id') !== null) {
            return;
        }
        $this->attributes['tahun_ajaran_id'] = $value;
    }

    protected static function booted()
    {
        static::creating(function ($tagihan) {
            // Snapshot class name if not already set
            if ($tagihan->kelas_id && !$tagihan->nama_kelas_snapshot) {
                $tagihan->nama_kelas_snapshot = $tagihan->kelas->nama_kelas ?? null;
            }
            // Snapshot student name
            $tagihan->nama_siswa_snapshot = $tagihan->siswa->nama ?? null;

            // Retrieve the tariff directly to ensure we have the correct amount even if the relation is not loaded
            $tarif = \App\Models\TarifPembayaran::find($tagihan->tarif_pembayaran_id);
            $tarifAmount = $tarif ? $tarif->nominal : 0;
            $tagihan->jumlah_tagihan = $tarifAmount;

            // Snapshot academic year
            $tagihan->tahun_ajaran_snapshot = $tagihan->tahun_ajaran_id;
        });

        // Ensure snapshot fields remain immutable after creation
        static::updating(function ($tagihan) {
            $original = $tagihan->getOriginal();
            $immutable = [
                'jumlah_tagihan',
                'nama_kelas_snapshot',
                'nama_siswa_snapshot',
                'tahun_ajaran_snapshot',
                'tarif_pembayaran_id',
            ];
            foreach ($immutable as $field) {
                if (array_key_exists($field, $original)) {
                    $tagihan->$field = $original[$field];
                }
            }
        });
    }

    protected function casts(): array
    {
        return [
            'jumlah_tagihan' => 'decimal:2',
            'jumlah_terbayar' => 'decimal:2',
            'tarif_lama' => 'decimal:2',
            'tanggal_jatuh_tempo' => 'date',
        ];
    }

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

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

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

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

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

    /**
     * Sisa tagihan yang belum terbayar
     */
   public function getSisaTagihanAttribute(): float
    {
        $tagihanEfektif = ($this->tarif_lama > 0)
            ? $this->tarif_lama
            : $this->jumlah_tagihan;

        return max(
            0,
            $tagihanEfektif - $this->jumlah_terbayar
        );
    }

    /**
     * Update status berdasarkan jumlah terbayar
     */
    public function updateStatus(): void
    {
        if ($this->sisa_tagihan <= 0) {
            $this->status = 'lunas';
        } elseif ($this->jumlah_terbayar > 0) {
            $this->status = 'kurang';
        } else {
            $this->status = 'belum_bayar';
        }
        $this->save();
    }

    /**
     * Apply a payment amount to this tagihan and update status.
     */
    public function applyPayment(float $amount): void
    {
        // Determine if this tagihan is of type DPP (non‑monthly)
        $isDpp = $this->tarifPembayaran && $this->tarifPembayaran->jenis === 'DPP';
        if ($isDpp) {
            // For DPP allow installments: add the paid amount to the accumulated total
            $this->jumlah_terbayar += $amount;
        } else {
            // For other types (e.g., SPP) set the paid amount directly
            $this->jumlah_terbayar = $amount;
        }
        $this->updateStatus();
    }

    /**
     * Label bulan dalam bahasa Indonesia
     */
    public function getNamaBulanAttribute(): ?string
    {
        if (!$this->bulan) return null;

        $bulan = [
            1 => 'Januari', 2 => 'Februari', 3 => 'Maret',
            4 => 'April', 5 => 'Mei', 6 => 'Juni',
            7 => 'Juli', 8 => 'Agustus', 9 => 'September',
            10 => 'Oktober', 11 => 'November', 12 => 'Desember',
        ];

        return $bulan[$this->bulan] ?? null;
    }

    /**
     * Scope: Semua tagihan belum lunas bisa dibayar.
     * Tidak ada pembatasan tanggal — tagihan bulan mendatang pun bisa dibayar di muka.
     */
    public function scopeBisaDibayar($query)
    {
        return $query;
    }

    /**
     * Scope: Tagihan yang sudah menjadi tunggakan berdasarkan kalender real-time server.
     *
     * SPP (punya kolom bulan):
     *   → Tunggakan jika tanggal_jatuh_tempo sudah lewat hari ini.
     *   → Jika tanggal_jatuh_tempo null, fallback: akhir bulan tagihan sudah lewat.
     *
     * DPP (bulan null):
     *   → Tunggakan jika tahun ajaran terkait sudah TIDAK aktif (jenjang berakhir).
     */
    public function scopeTunggakan($query)
    {
        $now = \Carbon\Carbon::now()->startOfDay();

        return $query->whereIn('tagihan.status', ['belum_bayar', 'kurang'])
            ->where(function ($q) use ($now) {
                // SPP: tagihan bulanan → tunggakan jika jatuh tempo sudah lewat
                $q->whereNotNull('bulan')
                  ->where(function ($inner) use ($now) {
                      $inner
                          // Jika punya tanggal_jatuh_tempo dan sudah lewat
                          ->where(function ($a) use ($now) {
                              $a->whereNotNull('tanggal_jatuh_tempo')
                                ->where('tanggal_jatuh_tempo', '<', $now);
                          })
                          // Jika tidak punya tanggal_jatuh_tempo, fallback ke akhir bulan tagihan
                          ->orWhere(function ($b) use ($now) {
                              $b->whereNull('tanggal_jatuh_tempo')
                                ->whereRaw("LAST_DAY(CONCAT(`tahun`, '-', LPAD(`bulan`, 2, '0'), '-01')) < ?", [$now->toDateString()]);
                          });
                  });
            });
    }

    /**
     * Mengecek apakah instance tagihan ini adalah tunggakan (real-time, per instance).
     *
     * SPP (bulan tidak null):
     *   → Tunggakan jika tanggal_jatuh_tempo sudah lewat hari ini.
     *   → Fallback: jika tidak ada tanggal_jatuh_tempo, cek apakah akhir bulan tagihan sudah lewat.
     *   → Tagihan bulan mendatang BUKAN tunggakan.
     *
     * DPP (bulan null):
     *   → Tunggakan jika tahun ajaran terkait sudah tidak aktif (jenjang selesai).
     *   → Selama tahun ajaran aktif, DPP yang belum lunas = "belum bayar" biasa.
     */
    public function getIsTunggakanAttribute(): bool
    {
        if ($this->status === 'lunas') {
            return false;
        }

        // ── SPP: Tagihan bulanan ──────────────────────────────────────────────
        if (!is_null($this->bulan)) {
            if (!is_null($this->tanggal_jatuh_tempo)) {
                return \Carbon\Carbon::parse($this->tanggal_jatuh_tempo)->startOfDay()->isPast();
            }
            // Fallback: jika tidak ada tanggal jatuh tempo, gunakan akhir bulan tagihan
            $akhirBulan = \Carbon\Carbon::create($this->tahun, $this->bulan, 1)->endOfMonth()->startOfDay();
            return $akhirBulan->isPast();
        }

        // ── DPP: Tagihan non-bulanan ──────────────────────────────────────────
        // DPP selamanya berstatus "belum lunas" (bukan tunggakan)
        return false;
    }
}
