<?php

namespace App\Models;

/**
 * One folio per stay (or a standalone folio for a non-staying client from
 * Phase 2 onward). `booking_id` is nullable and `guest_id` is carried
 * directly — see the CreateFoliosTable migration for why.
 */
class FolioModel extends TenantModel
{
    protected $table            = 'folios';
    protected $primaryKey       = 'id';
    protected $useAutoIncrement = true;
    protected $returnType       = 'object';
    protected $useTimestamps    = true;

    protected $allowedFields = [
        'booking_id',
        'guest_id',
        'corporate_account_id',
        'status',
        'opened_at',
        'closed_at',
    ];

    /**
     * Tags a folio as billable to a corporate account for the monthly
     * statement PDF — independent of split-billing payer_allocations.
     */
    public function setCorporateAccount(int $folioId, ?int $corporateAccountId): bool
    {
        return $this->update($folioId, ['corporate_account_id' => $corporateAccountId]);
    }

    public function openForBooking(int $bookingId, int $guestId): int
    {
        return (int) $this->insert([
            'booking_id' => $bookingId,
            'guest_id'   => $guestId,
            'status'     => 'open',
            'opened_at'  => date('Y-m-d H:i:s'),
        ], true);
    }

    /**
     * A standalone folio not linked to any booking — Phase 2's walk-in
     * restaurant guests and event clients who never have a booking_id.
     * Same table/shape as a stay folio (see CreateFoliosTable), just with
     * booking_id left null.
     */
    public function openStandalone(int $guestId): int
    {
        return (int) $this->insert([
            'booking_id' => null,
            'guest_id'   => $guestId,
            'status'     => 'open',
            'opened_at'  => date('Y-m-d H:i:s'),
        ], true);
    }

    /**
     * The guest's currently open folio from an active stay — this is what
     * "charge to room" posts against (see PosOrderModel::settleToRoom()).
     * Prefers a booking-linked folio (an actual in-house stay) over any
     * other open folio the guest might incidentally have.
     */
    public function findOpenForGuest(int $guestId): ?object
    {
        return $this->where('guest_id', $guestId)
            ->where('status', 'open')
            ->orderBy('booking_id', 'desc') // non-null (stay) folios first
            ->first();
    }

    public function close(int $folioId): bool
    {
        return $this->update($folioId, [
            'status'    => 'closed',
            'closed_at' => date('Y-m-d H:i:s'),
        ]);
    }

    public function findForBooking(int $bookingId): ?object
    {
        return $this->where('booking_id', $bookingId)->first();
    }

    public function totalCharges(int $folioId): string
    {
        $result = $this->db->table('folio_line_items')
            ->selectSum('amount')
            ->where('folio_id', $folioId)
            ->get()
            ->getRow();

        return $result->amount ?? '0.00';
    }

    /**
     * Net amount actually paid — real payments minus real refunds (see
     * AddIsRefundToFolioPayments). A refund is a normal folio_payments
     * row with is_refund=1, so it shows in the same ledger/history as
     * everything else, just subtracted here instead of added.
     */
    public function totalPaid(int $folioId): string
    {
        $paid = $this->db->table('folio_payments')
            ->selectSum('amount')
            ->where('folio_id', $folioId)
            ->where('is_refund', 0)
            ->get()
            ->getRow();

        $refunded = $this->db->table('folio_payments')
            ->selectSum('amount')
            ->where('folio_id', $folioId)
            ->where('is_refund', 1)
            ->get()
            ->getRow();

        return money_subtract($paid->amount ?? '0.00', $refunded->amount ?? '0.00');
    }

    public function totalRefunded(int $folioId): string
    {
        $result = $this->db->table('folio_payments')
            ->selectSum('amount')
            ->where('folio_id', $folioId)
            ->where('is_refund', 1)
            ->get()
            ->getRow();

        return $result->amount ?? '0.00';
    }

    public function balanceDue(int $folioId): string
    {
        return money_subtract($this->totalCharges($folioId), $this->totalPaid($folioId));
    }
}
