<?php

namespace App\Services;

use App\Enums\CampaignStatus;
use App\Models\Ad;
use App\Models\User;
use Illuminate\Support\Facades\DB;

/**
 * Operasi campaign (RPD FR-CAMP, NFR-04).
 *
 * Model budget harian: campaign TIDAK dibayar upfront — saldo dipotong per-klik
 * inline di LandingController::go() sesuai budget_harian (convert via rate).
 * Mutasi saldo hanya terjadi di alur klik (Out) dan top-up/billing (In).
 */
class CampaignService
{
    public function __construct(
        private readonly CreditService $creditService,
    ) {}

    /**
     * Buat campaign: status Pending, budget_harian (Rp) tercatat.
     * Tidak ada debit upfront — saldo dipotong per-klik saat tayang.
     *
     * @param  array<string, mixed>  $data
     */
    public function create(User $user, array $data): Ad
    {
        // Saldo harus cukup minimal 1 credit — iklan dibayar per-klik inline.
        if ($user->credit < 1) {
            throw new \DomainException('Saldo credit habis. Top-up dahulu sebelum membuat campaign.');
        }

        return DB::transaction(function () use ($user, $data): Ad {
            $ad = Ad::create([
                'judul' => $data['judul'],
                'url' => $data['url'],
                'jam_tayang_mulai' => $data['jam_tayang_mulai'],
                'jam_tayang_selesai' => $data['jam_tayang_selesai'],
                'credit' => 0,
                'budget_harian' => $data['budget_harian'],
                'user_id' => $user->id,
                'status' => CampaignStatus::Pending,
                'klik' => 0,
                'klik_hari_ini' => 0,
                'klik_tanggal' => null,
                'rollover_klik' => 0,
                'pause_reason' => null,
                'muncul' => 0,
                'ad_type' => $data['ad_type'] ?? 'affiliate',
                'ad_platform_id' => $data['ad_platform_id'] ?? null,
                'target_country' => $data['target_country'] ?? null,
            ]);

            // Attach kategori (many-to-many, bisa lebih dari satu).
            if (! empty($data['kategori_ids'])) {
                $ad->kategoris()->sync($data['kategori_ids']);
            }

            return $ad;
        });
    }

    /**
     * Approve campaign: status Aktif, isi tgl_aktif (RPD FR-CAMP-05).
     */
    public function approve(Ad $ad): Ad
    {
        if ($ad->status !== CampaignStatus::Pending) {
            throw new \DomainException('Hanya campaign pending yang dapat disetujui.');
        }

        if ($ad->kategoris->isEmpty()) {
            throw new \DomainException('Campaign belum memiliki kategori. Pilih kategori sebelum menyetujui.');
        }

        $ad->update([
            'status' => CampaignStatus::Aktif,
            'tgl_aktif' => now(),
        ]);

        return $ad->fresh();
    }

    /**
     * Pause (stop) campaign Aktif: berhenti tayang, pause_reason manual.
     * Saldo tidak berubah (budget harian, debit per-klik).
     */
    public function pause(Ad $ad): Ad
    {
        if ($ad->status !== CampaignStatus::Aktif) {
            throw new \DomainException('Hanya campaign aktif yang dapat dihentikan.');
        }

        if ($ad->is_paused) {
            throw new \DomainException('Campaign sudah dihentikan.');
        }

        $ad->update(['is_paused' => true, 'pause_reason' => 'manual']);

        return $ad->fresh();
    }

    /**
     * Resume (start) campaign yang di-pause: kembali tayang di rotator.
     * Tolak bila saldo user habis (pause_reason='no_saldo') — harus top-up dulu.
     */
    public function resume(Ad $ad): Ad
    {
        if ($ad->status !== CampaignStatus::Aktif) {
            throw new \DomainException('Hanya campaign aktif yang dapat dimulai.');
        }

        if (! $ad->is_paused) {
            throw new \DomainException('Campaign sedang aktif berjalan.');
        }

        if ($ad->user->credit < 1) {
            throw new \DomainException('Saldo credit habis. Top-up dahulu sebelum memulai campaign.');
        }

        $ad->update(['is_paused' => false, 'pause_reason' => null]);

        return $ad->fresh();
    }

    /**
     * Jadikan campaign Draft: simpan tanpa tayang, credit tetap terkunci
     * (mirip draft WordPress — bisa di-restore kembali ke Aktif).
     */
    public function toDraft(Ad $ad): Ad
    {
        if (! in_array($ad->status, [CampaignStatus::Pending, CampaignStatus::Aktif], true)) {
            throw new \DomainException('Campaign ini tidak dapat dijadikan draft.');
        }

        $ad->update(['status' => CampaignStatus::Draft]);

        return $ad->fresh();
    }

    /**
     * Restore campaign Draft → langsung Aktif (skip re-approve admin).
     * Credit terkunci dipakai kembali, tgl_aktif diisi bila kosong.
     */
    public function restore(Ad $ad): Ad
    {
        if ($ad->status !== CampaignStatus::Draft) {
            throw new \DomainException('Hanya campaign draft yang dapat diaktifkan kembali.');
        }

        $ad->update([
            'status' => CampaignStatus::Aktif,
            'tgl_aktif' => $ad->tgl_aktif ?? now(),
            'is_paused' => false,
        ]);

        return $ad->fresh();
    }

    /**
     * Hapus campaign. Tidak ada refund — model budget harian, saldo dipotong
     * per-klik (tidak ada credit terkunci di campaign).
     */
    public function delete(Ad $ad): void
    {
        $ad->delete();
    }
}
