<?php
/**
 * Ajax request for processing a refund request
 * This script is used to process the refund form displayed in admin
 *
 * @author Martins Fridenbergs <martins.fridenbergs@mtcmedia.co.uk>
 */

namespace Mtc\Plugins\Refunds\Classes;

use App\Events\OrderRefundedEvent;
use Illuminate\Support\Facades\Event;
use Mtc\Core\Models\CountryState;
use Mtc\Core\Response;
use Mtc\Shop\EmailTemplate;
use Mtc\Shop\Order;

$path = '../../../';
require_once $path . 'core/includes/header.inc.php';
$errors = [];

if (filter_input(INPUT_POST, 'action') === 'do_refund') {
    $order = $_POST['order'];
    $shop_order = new \Order($order['id']);
    $refund_data = Refund::prepareRefundDataObject($order['id']);
    $refund_methods = collect(Event::dispatch('refunds.get_refund_methods', $shop_order))->filter();

    // Make sure that the refund method has been selected
    if (empty($order['refund_method'])) {
        $errors['refund_method'] = 'Please choose a refund method';
    }
    // Make sure that the refund method has been selected
    if (empty($order['refund_template'])) {
        $errors['refund_template'] = 'Please choose a refund template';
    }

    // Make sure that reference has been set
    if (empty($order['refund_reference'])) {
        $errors['refund_reference'] = 'Please set Refund Reference';
    }

    // get the refund amounts
    $delivery_refund = $order['delivery_refund_amount'] ?? 0;
    $refund_item_total = collect($order['items'])->sum('to_refund_amount');
    $total_refund_amount = $delivery_refund + $refund_item_total;

    if (round($total_refund_amount, 2) > round($refund_data->cost_total - $refund_data->already_refunded_amount, 2)) {
        $errors['partial_refund'] = "Amount to be refunded exceeds allowed amount";
    }

    if ($total_refund_amount == 0) {
        $errors['partial_refund_zero'] = "You must select at least one item from the item line to be refunded.";
    }

    // We also need to double confirm items are not exceeding their allowed quantities
    // This could be caused by ajax error or multiple people doing the same refund at once
    $refund_data->items->each(function ($item) use (&$errors, $order) {
        $refund_item = collect($order['items'])
            ->where('id', $item['id'])
            ->first();

        if ($refund_item['quantity_to_refund'] > $item->max_quantity_to_refund) {
            $errors['item_' . $item->id] = "{$item->item_name} quantity is set higher than refundable quantity";
        }
    });

    if (count($errors) == 0) {
        // No errors, we can set up refund for this order
        // Start by creating a refunding line which stores order_id adn delivery amount
        $refund = Refund::create([
            'order_id' => $refund_data->id,
            'delivery_refund_amount' => $delivery_refund,
            'reference' => $order['refund_reference'],
            'note' => $order['refund_note'] ?? ''
        ]);

        // After setting up main refund object proceed to order items.
        // Refund each line that has been set to be refunded with specific quantities
        collect($order['items'])
            ->each(function ($item) use ($refund) {
                $refund->items()->create([
                    'order_id' => $refund->order_id,
                    'order_item_id' => $item['id'],
                    'item_id' => $item['item_id'],
                    'quantity' => $item['quantity_to_refund'],
                    'amount_refunded' => $item['to_refund_amount']
                ]);
            });

        // Add note to order stating refund amount
        $refund_data->notes()->create([
            'note' => $adminuser->user['name'] . " has refunded £{$total_refund_amount} on this order "
                . "with the following reference '{$order['refund_reference']}' and  message: {$order['refund_note']}"
        ]);

        // Fetch the information about the order again to find out if all items have been refunded yet
        $refund_data = Refund::prepareRefundDataObject($order['id']);

        // Since we're using the prepare data object method we already know if each or the order line rows
        // are refunded so we can use them to confirm if all order lines have been refunded
        $fully_refunded = $refund_data->items->filter(function ($item) {
                return $item->is_fully_refunded;
            })->count() == $refund_data->items->count();

        // Update order status
        $newStatus = $fully_refunded ?
            Order::STATUS_REFUNDED :
            Order::STATUS_PART_REFUNDED;
        $shop_order->updateStatus($newStatus);

        $template = EmailTemplate::query()->find($order['refund_template']);
        $options = [
            'order' => $shop_order, // order details
            'order_total' => $shop_order->getTotalCost(), // orders total cost
            'line_items' => $shop_order->getLineItems(), // Order items with additional up to date information pulled from the item class
            'order_subtotal' => $shop_order->getSubtotalCost(),
            'vat_cost' => $shop_order->getVatCost(), // Total VAT cost of the order
            'order_date' => $shop_order->getOrderDate('Y-m-d H:i:s'), // order date sent in full... twig can re-render into any date format from here
            'delivery_cost' => $shop_order->getDelivery()->cost, // Delivery Cost
            'delivery_name' => $shop_order->getDelivery()->name, // Delivery Name
            'order_status_text' => $shop_order->getOrderStatusText(0), // Order status text as defined in admin under shop/order status text
            'html' => $template->template,
            'subject' => $template->title,
            'countries' => $countries, // Country name
            'state_list' => CountryState::getCountryStateList(),
        ];

        $email_body = $twig->render('emails/shop/contact_email.twig', $options);

        $subject = "Your " . config('app.name') . " Order: " . $orders_statuses[$shop_order->status];

        email($shop_order->info['email'], $subject, $email_body);


        /*
         * Check if we do have to do refund on the site end
         * This is done by finding the refund method user chose
         * and checking if the callback for this method exists and is callable
         * If the callback is valid we call it by passing order id and total amount that was refunded
         */
        $refund_method = $refund_methods->where('id', $order['refund_method'])->first();
        if (!empty($refund_method['callback']) && method_exists(...explode('::', $refund_method['callback']))) {
            call_user_func_array($refund_method['callback'], [$order['id'], $total_refund_amount]);
        }

        Event::dispatch(OrderRefundedEvent::class, new OrderRefundedEvent($shop_order, $total_refund_amount));

        // Set session variable to display message that order has been refunded
        $_SESSION['message'] = 'Order has been ' . ($fully_refunded ? 'fully' : 'part') . ' refunded';

        Response::outputJson();
    } else {
        Response::outputJson('failed', [], $errors);
    }
}
