<?php

namespace Modules\VetAdmission\Entities\Services;

use App\Product;
use App\Variation;
use App\Transaction;
use App\TransactionSellLine;
use App\Utils\ProductUtil;
use App\Utils\TransactionUtil;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;
use Modules\VetAdmission\Entities\VaAdmission;
use Modules\VetAdmission\Entities\VaAdmissionMedication;
use Modules\VetAdmission\Entities\VaCareLog;
use Modules\VetAdmission\Entities\VaCareTaskType;

/**
 * Roll up an admission's billable events into an ERP sell. Each completed
 * care log (whose task type has a product_id), each fluid bag, and each
 * day-of-stay produces one sell line. The first call creates a draft
 * transaction; subsequent calls append to the same one until discharge
 * flips it to final.
 */
class AdmissionBillingService
{
    /** Get or create the draft sell that backs this admission. */
    public function getOrCreateDraftSell(VaAdmission $a): Transaction
    {
        if ($a->erp_sell_id) {
            $trx = Transaction::find($a->erp_sell_id);
            if ($trx) return $trx;
        }

        $business = \App\Business::find($a->business_id);
        $locationId = $business?->locations()->first()->id ?? null;

        $trx = Transaction::create([
            'business_id'      => $a->business_id,
            'location_id'      => $locationId,
            'type'             => 'sell',
            'status'           => 'draft',
            'sub_status'       => null,
            'invoice_no'       => 'ADM-' . $a->admission_no,
            'contact_id'       => $a->contact_id,
            'transaction_date' => now(),
            'total_before_tax' => 0,
            'final_total'      => 0,
            'created_by'       => auth()->id() ?? $a->created_by,
        ]);

        $a->update(['erp_sell_id' => $trx->id]);
        return $trx;
    }

    /** Append a sell line for a single product + qty. Idempotent on (transaction, product, source_ref). */
    public function appendLine(Transaction $trx, int $productId, float $qty, ?string $description = null): ?TransactionSellLine
    {
        $product = Product::with('variations')->find($productId);
        if (!$product) return null;

        $variation = $product->variations->first();
        if (!$variation) return null;

        $unitPrice = (float) ($variation->sell_price_inc_tax ?? $variation->default_sell_price ?? 0);

        $line = TransactionSellLine::create([
            'transaction_id'       => $trx->id,
            'product_id'           => $product->id,
            'variation_id'         => $variation->id,
            'quantity'             => $qty,
            'unit_price'           => $unitPrice,
            'unit_price_inc_tax'   => $unitPrice,
            'item_description'     => $description,
        ]);

        // Bump totals
        $trx->refresh();
        $sum = TransactionSellLine::where('transaction_id', $trx->id)
            ->selectRaw('SUM(quantity * unit_price_inc_tax) AS s')->value('s');
        $trx->update([
            'total_before_tax' => $sum,
            'final_total'      => $sum,
        ]);

        return $line;
    }

    /**
     * Post the per-day-of-stay line for every midnight since
     * last_daily_post_date (or admitted_at). Called by the daily cron.
     */
    public function postDailyStay(VaAdmission $a, Carbon $upto): int
    {
        if ($a->status !== 'admitted' || !$a->daily_rate || !$a->daily_rate_product_id) return 0;

        $cursor = $a->last_daily_post_date
            ? Carbon::parse($a->last_daily_post_date)->addDay()
            : Carbon::parse($a->admitted_at)->startOfDay();

        if ($cursor->gt($upto)) return 0;

        $trx = $this->getOrCreateDraftSell($a);
        $count = 0;
        while ($cursor->lte($upto)) {
            $this->appendLine($trx, (int) $a->daily_rate_product_id, 1,
                'Boarding · ' . $cursor->format('d M Y'));
            $count++;
            $cursor->addDay();
        }
        $a->update(['last_daily_post_date' => $upto->toDateString()]);
        return $count;
    }

    /** Settle the admission: finalise the draft sell + return it. */
    public function finaliseDischarge(VaAdmission $a): ?Transaction
    {
        if (!$a->erp_sell_id) return null;
        $trx = Transaction::find($a->erp_sell_id);
        if (!$trx) return null;

        $trx->update([
            'status'           => 'final',
            'transaction_date' => $a->discharged_at ?: now(),
        ]);
        return $trx;
    }
}
