<?php

namespace Mtc\Shop;

use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Mtc\Plugins\Refunds\Classes\RefundItem;

class Sales
{
    public Carbon $startDate;
    public Carbon $endDate;

    public function __construct(Carbon $startDate, Carbon $endDate)
    {
        $this->startDate = $startDate;
        $this->endDate = $endDate;
    }

    /**
     * Gets a base query for sales data.
     *
     * @return Builder
     */
    protected function getQuery(): Builder
    {
        return Order::query()
            ->where('paid', '1')
            ->whereNotIn('status', [
                Order::STATUS_REFUNDED,
                Order::STATUS_CANCELLED,
            ])
            ->whereBetween('date', [
                $this->startDate,
                $this->endDate,
            ]);
    }

    /**
     * Total order value over a period of time
     *
     * @return int|float
     */
    public function getTotalSalesRevenue(): int|float
    {
        return $this->getQuery()
            ->sum('cost_total');
    }

    /**
     * Calculates the total sales revenue per day for a given date range.
     *
     * @return float|int The average sales revenue per day.
     */
    public function getTotalSalesRevenuePerDay(): float|int
    {
        return $this->getTotalSalesRevenue() / $this->getDaysInPeriod();
    }

    /**
     * Retrieves the total number of orders.
     *
     * @return int The total count of orders.
     */
    public function getNumberOfOrders(): int
    {
        return $this->getQuery()
            ->count();
    }

    /**
     * Calculates the average number of orders per day for a specified period.
     *
     * @return int|float The average number of orders per day
     */
    public function getNumberOfOrdersPerDay(): int|float
    {
        return $this->getNumberOfOrders() / $this->getDaysInPeriod();
    }

    /**
     * Calculates the total number of days within the specified period,
     * including both the start and end dates.
     *
     * @return int The total number of days in the period.
     */
    public function getDaysInPeriod(): int
    {
        return $this->startDate->diffInDays($this->endDate) + 1;
    }

    /**
     * Calculates the total refund amount for a specified date range.
     *
     * @return int|float The sum of refunded amounts within the given date range.
     */
    public function getRefundAmount(): int|float
    {
        return RefundItem::query()
            ->whereBetween('created_at', [$this->startDate, $this->endDate])
            ->sum('amount_refunded');
    }

    /**
     * Retrieves the total number of items refunded within a specified date range.
     *
     * @return int The total quantity of refunded items for the given date range.
     */
    public function getNumberOfItemsRefunded(): int
    {
        return RefundItem::query()
            ->whereBetween('created_at', [$this->startDate, $this->endDate])
            ->sum('quantity');
    }

    /**
     * Calculates the average value of orders.
     *
     * @return int|float The average order value, or 0 if there are no orders.
     */
    public function getAverageOrderValue(): int|float
    {
        $numberOfOrders = $this->getNumberOfOrders();
        if ($numberOfOrders === 0) {
            return 0;
        }

        return $this->getTotalSalesRevenue() / $numberOfOrders;
    }

    /**
     * Retrieves the total number of items sold.
     *
     * @return float The sum of quantities sold across all order items.
     */
    public function getNumberOfItemsSold(): float
    {
        return $this->getQuery()
            ->join('order_items', 'order_items.order_id', '=', 'order.id')
            ->sum('order_items.quantity');
    }
}
