<?php

namespace App\Services;

use App\Models\ClientDomain;
use App\Models\ClientProduct;
use App\Models\Invoice;
use App\Models\Order;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

/**
 * PRC-642 — Sipariş iptali için TEK merkez.
 *
 * Sipariş status'unu 'cancelled' yapar (OrderObserver `CancelOrder` hook'unu
 * tetikler) ve henüz provizyon edilmemiş yan-kayıtları temizler: bağlı
 * fatura + bekleyen ClientProduct / ClientDomain satırları. Hem admin "reject"
 * akışı hem de günlük `orders:cancel-unpaid` cron'u buradan geçer — iptal
 * yan-etkileri tek yerde toplanır (kopyalanmaz).
 */
class OrderCancellationService
{
    /**
     * Siparişi iptal et ve yan-kayıtları temizle.
     *
     * @param  string  $source  'admin' | 'auto' | 'system' — log/izlenebilirlik için
     * @return bool  iptal uygulandıysa true, zaten iptal/iptal edilemez ise false
     */
    public function cancel(Order $order, string $reason = '', string $source = 'system'): bool
    {
        if (in_array($order->status, ['cancelled', 'refunded'], true)) {
            return false;
        }

        return DB::transaction(function () use ($order, $reason, $source) {
            // 1) Sipariş status → cancelled. OrderObserver::updated() bu değişimde
            //    `CancelOrder` action hook'unu tetikler (modül cancel handler'ları).
            $order->update(['status' => 'cancelled']);

            // 2) Bağlı faturayı iptal et (ödenmiş/iptal/iade değilse).
            $invoice = $order->invoice;
            if ($invoice && ! in_array($invoice->status, ['paid', 'cancelled', 'refunded'], true)) {
                $invoice->update([
                    'status' => 'cancelled',
                    'cancel_date' => now(),
                ]);
            }

            // 3) Bekleyen (henüz provizyon edilmemiş) ClientProduct'ları sonlandır.
            //    order_id ile doğrudan bağlı. Proje konvansiyonu: iptal/terminate =
            //    status 'terminated' (client_products.status enum'unda 'cancelled' yok;
            //    bkz. ProcessPendingCancellationsCommand).
            $cancelledProducts = ClientProduct::where('order_id', $order->id)
                ->where('status', 'pending')
                ->update(['status' => 'terminated', 'terminate_date' => now()]);

            // 4) Bekleyen ClientDomain'leri kaldır. Domain'lerin order_id'si yok;
            //    fatura kalemlerindeki related_service_id üzerinden bağlanır.
            //    client_domains.status enum'unda terminal 'cancelled' yok → phantom
            //    (hiç provizyon edilmemiş) satır soft-delete edilir (SoftDeletes).
            $domainIds = [];
            if ($invoice) {
                $domainIds = $invoice->items()
                    ->where('related_type', 'domain')
                    ->whereNotNull('related_service_id')
                    ->pluck('related_service_id')
                    ->all();
            }
            $cancelledDomains = 0;
            if (! empty($domainIds)) {
                $cancelledDomains = ClientDomain::whereIn('id', $domainIds)
                    ->where('status', 'pending')
                    ->delete();
            }

            // 5) Kupon/promosyon kullanımını geri al. Sipariş-oluşturma anında
            //    (ödeme öncesi) tüketilen usage sayaçları + ledger satırları burada
            //    (TERMINAL, ödeme-dışı iptal olayında) geri alınır — retry edilebilir
            //    başarısız ödeme denemesinde DEĞİL. Gate: fatura {unpaid|cancelled}
            //    VE sipariş {pending|cancelled}. Best-effort: revert hatası iptali
            //    ASLA kırmaz.
            $invoiceStatus = $invoice ? $invoice->status : null;
            $revertGate = in_array($order->status, ['pending', 'cancelled'], true)
                && (! $invoice || in_array($invoiceStatus, ['unpaid', 'cancelled'], true));
            if ($revertGate) {
                try {
                    app(DiscountUsageReverter::class)->revertForOrder($order);
                } catch (\Throwable $e) {
                    Log::warning('Discount usage revert failed on order cancel', [
                        'order_id' => $order->id,
                        'error' => $e->getMessage(),
                    ]);
                }
            }

            Log::info('Order cancelled', [
                'order_id' => $order->id,
                'order_number' => $order->order_number,
                'client_id' => $order->client_id,
                'source' => $source,
                'reason' => $reason,
                'invoice_cancelled' => (bool) ($invoice && $invoice->status === 'cancelled'),
                'products_cancelled' => $cancelledProducts,
                'domains_cancelled' => $cancelledDomains,
            ]);

            return true;
        });
    }
}
