<?php

use Mtc\Core\Permissions;
use Mtc\Plugins\DeliveryCouriers\Classes\Courier;
use Mtc\Shop\Admin\ViewHelper;
use Mtc\Shop\Order\Item as OrderItem;
use App\Admin\OrdersHelper;
use Mtc\Shop\Order\Shipment;
use Mtc\Core\Admin\User as AdminUserModel;
use Mtc\Shop\Order\Protx;
use Carbon\Carbon;

use Illuminate\Support\Facades\DB;

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

init_request_values([
        'startnumber' => null,
        'category' => null,
        'status' => null,
        'coupon' => null,
        'id' => null,
        'postcode' => null,
        'firstname' => null,
        'lastname' => null,
        'email' => null,
        'start_date' => null,
        'end_date' => null,
        'type' => null,
        'trustpilot_status' => null,
        'orderbyfield' => null,
        'orderbydir' => null,
        'order_type' => null,
]);

$view_state_keys = ViewHelper::$view_state_keys;

$append = '';

$admin = AdminUserModel::getAdminUser();
$allowed_categories = $admin ? $admin->allowedCategories->pluck('category_id') : collect();

$page_meta['title'] = 'Manage Orders';

$queryParams = $_REQUEST;

$type = $order_filter_type = filter_input(INPUT_GET, 'order_type');
$per_page = (filter_input(INPUT_GET, 'per_page') ?? 20);
if ($per_page > 250) {
    $per_page = 250;
}
if (!$per_page) {
    $per_page = 20;
}
$queryParams['per_page'] = $per_page;

$errors = [];
$warnings = [];

$startnumber = 0;
if (is_numeric($_REQUEST['startnumber'])) {
    $startnumber = $_REQUEST['startnumber'];
}


// Mark Orders as shipped
if (filter_input(INPUT_POST, 'processing_action') === 'ship' && !empty($_POST['process_orders'])) {
    foreach (array_keys($_POST['process_orders']) as $id) {
        // Confirm order exists
        if (Mtc\Shop\Order::where('id', $id)->count() == 0) {
            continue;
        }

        $order = new Order($id);
        if ($order->status === \Mtc\Shop\Order::STATUS_SHIPPED) {
            // If order already shipped, skip over it
            continue;
        }
        $order->updateStatus(\Mtc\Shop\Order::STATUS_SHIPPED);

        // Create shipment
        Shipment::create([
                'order_id' => $order->getId(),
                'date' => date('Y-m-d')
        ]);
    }
    header('Refresh: 0');
    exit();
}

if (filter_input(INPUT_POST, 'bulk_status_change') !== '' && !empty($_POST['process_orders'])) {
    $new_status = (int)filter_input(INPUT_POST, 'bulk_status_change');
    $order_ids = array_keys($_POST['process_orders']);
    foreach ($order_ids as $id) {

        if (Mtc\Shop\Order::where('id', $id)->count() === 0) {
            continue;
        }

        $order = new Order($id);
        $order->updateStatus($new_status);
    }
    header('Refresh: 0');
    exit();
}

// Generate parcelforce export for selected orders
if (filter_input(INPUT_POST, 'processing_action') === 'parcelforce' && !empty($_POST['process_orders'])) {
    $order_ids = array_keys($_POST['process_orders']);
    $courier = new ParcelForce();
    $courier->generateExport($order_ids);
    exit();
}

// Print dispatch notes for selected orders
if (filter_input(INPUT_POST, 'processing_action') === 'dispatch_notes' && !empty($_POST['process_orders'])) {
    $order_ids = array_keys($_POST['process_orders']);
    $url = '/shop/admin/orders/order.printorder.php?id=' . implode(',', $order_ids);
    header("Location: " . $url);
    exit();
}

// Print prescriptions for selected orders
if (filter_input(INPUT_POST, 'processing_action') === 'print_prescriptions' && !empty($_POST['process_orders'])) {

    header("Content-type:application/pdf");
    echo Order::batchPrescriptions(array_keys($_POST['process_orders']), $basket_countries, $twig);
    exit();
}

if (filter_input(INPUT_POST, 'processing_action') === 'print_pharmacy_labels' && !empty($_POST['process_orders'])) {
    $order_ids = array_keys($_POST['process_orders']);
    $url = "/shop/admin/orders/optimized_print.php?order_ids=" . implode(',', $order_ids);
    header("Location: " . $url);
    exit();
}

if (filter_input(INPUT_POST, 'processing_action') === 'capture_payments' && !empty($_POST['process_orders'])) {
    require_once SITE_PATH . '/shop/checkout/server/function.sagepay.php';
    \Mtc\Shop\Order\Protx::bulkCapturePayments(array_keys($_POST['process_orders']), $strReleaseURL);
    header('Refresh: 0');
    exit();
}

// Print invoices for selected orders
if (filter_input(INPUT_POST, 'processing_action') === 'print_invoices' && !empty($_POST['process_orders'])) {

    //header("Content-type:application/pdf");
    echo Order::batchInvoices(array_keys($_POST['process_orders']), $basket_countries, $twig);
    exit();
}

// Print optimized labels for selected orders
if (filter_input(INPUT_POST, 'processing_action') === 'optimized_labels' && !empty($_POST['process_orders'])) {
    $order_ids = implode(',', array_keys($_POST['process_orders']));
    header('Location: optimized_print.php?order_ids=' . $order_ids);
    exit();
}

// Bulk create shipments
if (filter_input(INPUT_POST, 'processing_action') === 'create_shipments' && !empty($_POST['process_orders'])) {
    $order_ids = array_keys($_POST['process_orders']);
    foreach ($order_ids as $order_id) {
        $order_to_ship = new Order($order_id);
        if ($order_to_ship->hasShipmentsWithLabels() === false) {
            // If there's a shipment but it doesn't have a label, we delete it and try create a new one
            if ($order_to_ship->hasShipments()) {
                Shipment::query()
                        ->where('order_id', '=', $order_id)
                        ->delete();
            }
            Courier::sendOrder($order_to_ship);
        }
    }
    header('Refresh: 0');
    exit();
}

// Trustpilot mass action
if (filter_input(INPUT_POST, 'processing_action') === 'trustpilot' && !empty($_POST['process_orders'])) {
    Order::batchTrustpilot($_POST['process_orders']);
    header("Refresh: 0");
    exit();
}

$orderbyfield = 'order.id';
$orderbydir = 'DESC';
$orderbydiropp = 'ASC';

if (!empty($_REQUEST['orderbyfield']) && !empty($_REQUEST['orderbydir'])) {
    $orderbyfield = str_replace('-', '.', $_REQUEST['orderbyfield']);
    $orderbydir = $_REQUEST['orderbydir'] === 'ASC' ?
            'ASC' :
            'DESC';
}

if (isset($queryParams['page'])) {
    unset($queryParams['page']);
}
$append = '&' . http_build_query($queryParams);

$orderbydiropp = $_REQUEST['orderbydir'] === 'ASC' ?
        'DESC' :
        'ASC';

$orderSortColumn = $orderbyfield;
$needsItemSort = $orderbyfield === 'item';

if ($needsItemSort) {
    $orderSortColumn = 'sort_item_name';
}


// If the user is doctor and the status is not one of the processing statuses, need to limit the results
$limit = '';

if (empty($_GET['page'])) {
    $_GET['page'] = 1;
}

$rowsPerPage = (int)$per_page;
$pageNum = $_GET['page'];
$offset = ($pageNum - 1) * $rowsPerPage;
$pagestoshow = 10;

$orderHelper = new OrdersHelper($_REQUEST);
$ordersQuery = $orderHelper->getOrdersBuilderQuery(filter_input(INPUT_GET, 'order_type'));

$orderIdsQuery = (clone $ordersQuery)
    ->select('order.id')
    ->distinct();

if ($needsItemSort) {
    $orderIdsQuery->selectSub(static function ($subQuery) {
        $subQuery->from('order_items')
            ->selectRaw('MIN(order_items.item_name)')
            ->whereColumn('order_items.order_id', 'order.id');
    }, 'sort_item_name');
}

$totalnumrows = (clone $orderIdsQuery)
    ->reorder()
    ->distinct()
    ->count('order.id');

$orderIds = collect();

if ($totalnumrows > 0) {
    $orderedIdsQuery = (clone $orderIdsQuery)
        ->orderBy($orderSortColumn, $orderbydir)
        ->orderByDesc('order.id')
        ->skip($offset)
        ->take($rowsPerPage);

    $orderIds = $orderedIdsQuery->pluck('order.id')->map(static fn($id) => (int)$id);
}

$orderData = collect();

if ($orderIds->isNotEmpty()) {
    $orderData = \Mtc\Shop\Order::query()
        ->with([
            'shipments',
            'customer',
            'customer.addressBilling',
            'customer.addressShipping',
            'address',
            'info',
            'items.shipmentItems',
            'items.item.brands',
            'items.item.categories',
            'items.item.sizes',
            'items.item.custom',
            'items.refund_items',
            'items.item_size',
            'subscriptions',
        ])
        ->whereIn('id', $orderIds->all())
        ->get();

    $positions = array_flip($orderIds->all());
    $orderData = $orderData
        ->sortBy(static function ($order) use ($positions) {
            return $positions[$order->id] ?? PHP_INT_MAX;
        })
        ->values();
}

$orderAddresses = collect();
$orderInfos = collect();
$orderItemsByOrder = collect();
$orderAdmins = collect();
$refundedItemsByOrder = [];

if ($orderData->isNotEmpty()) {
    $orderIds = $orderData->pluck('id')->map(static fn($id) => (int)$id)->all();
    $orderAdminIds = $orderData->pluck('admin_id')->filter()->unique()->map(static fn($id) => (int)$id);

    $orderAddresses = $orderData->mapWithKeys(static function ($order) {
        $addresses = $order->address
            ->keyBy('type')
            ->map(static fn($address) => $address->toArray())
            ->toArray();

        return [$order->id => empty($addresses) ? null : $addresses];
    });

    $orderInfos = $orderData->mapWithKeys(static function ($order) {
        $info = $order->info ? $order->info->toArray() : [];
        return [$order->id => $info];
    });

    $orderItemsByOrder = $orderData->mapWithKeys(static function ($order) {
        return [$order->id => $order->items->sortBy('id')];
    });

    if ($orderAdminIds->isNotEmpty()) {
        $orderAdmins = AdminUserModel::query()
            ->whereIn('id', $orderAdminIds->all())
            ->get()
            ->keyBy('id');
    }

    $refundedItemsByOrder = $orderData->mapWithKeys(static function ($order) {
        $refundedIds = $order->items
            ->flatMap(static function ($item) {
                return $item->refund_items
                    ->where('quantity', '>', 0)
                    ->pluck('order_item_id');
            })
            ->unique()
            ->map(static fn($orderItemId) => (int)$orderItemId)
            ->values()
            ->all();

        return [$order->id => $refundedIds];
    })->all();

    $orderedWithinCounts = [];
    $memberIds = $orderData->pluck('member')->filter(static fn($member) => (int)$member > 0)->unique()->values();
    if ($memberIds->isNotEmpty()) {
        $orderDates = $orderData->mapWithKeys(static function ($order) {
            return [$order->id => $order->date ? strtotime($order->date) : null];
        });

        $orderDateValues = array_filter($orderDates->all(), static fn($timestamp) => $timestamp !== null && $timestamp !== false);

        if (!empty($orderDateValues)) {
            $minDate = Carbon::createFromTimestamp(min($orderDateValues))->subDays(28);
            $maxDate = Carbon::createFromTimestamp(max($orderDateValues));

            $recentOrders = \Mtc\Shop\Order::query()
                ->select(['id', 'member', 'date'])
                ->whereIn('member', $memberIds->all())
                ->where('paid', 1)
                ->where('failed', 0)
                ->whereBetween('date', [$minDate->format('Y-m-d H:i:s'), $maxDate->format('Y-m-d H:i:s')])
                ->orderBy('member')
                ->orderBy('date')
                ->orderBy('id')
                ->get();

            $ordersByMember = [];
            foreach ($recentOrders as $recentOrder) {
                $timestamp = $recentOrder->date ? strtotime($recentOrder->date) : null;
                if ($timestamp === null) {
                    continue;
                }
                $ordersByMember[$recentOrder->member][] = [
                    'id' => (int)$recentOrder->id,
                    'timestamp' => $timestamp,
                ];
            }

            $orderIdSet = array_fill_keys($orderIds, true);
            foreach ($ordersByMember as $memberOrderList) {
                $start = 0;
                $count = count($memberOrderList);
                for ($i = 0; $i < $count; $i++) {
                    $current = $memberOrderList[$i];
                    $threshold = $current['timestamp'] - (28 * 86400);
                    while ($start < $i && $memberOrderList[$start]['timestamp'] < $threshold) {
                        $start++;
                    }
                    if (!isset($orderIdSet[$current['id']])) {
                        continue;
                    }
                    $previousCount = 0;
                    for ($j = $start; $j < $i; $j++) {
                        if ($memberOrderList[$j]['id'] < $current['id']) {
                            $previousCount++;
                        }
                    }
                    $orderedWithinCounts[$current['id']] = $previousCount;
                }
            }
        }
    }

    $protxPayments = Protx::query()
        ->whereIn('order_id', $orderIds)
        ->get()
        ->keyBy('order_id')
        ->map->toArray();

    $evopayPayments = collect(DB::table('order_evopay')->whereIn('order_id', $orderIds)->get())
        ->keyBy('order_id')
        ->map(fn($payment) => (array)$payment);

    $paypalPayments = collect(DB::table('order_paypal')->whereIn('order_id', $orderIds)->get())
        ->keyBy('order_id')
        ->map(fn($payment) => (array)$payment);

    $worldpayPayments = collect(DB::table('order_worldpay')->whereIn('order_id', $orderIds)->get())
        ->keyBy('order_id')
        ->map(fn($payment) => (array)$payment);

    $stripePayments = collect(DB::table('order_stripe')->whereIn('order_id', $orderIds)->get())
        ->keyBy('order_id')
        ->map(fn($payment) => (array)$payment);

    $realexPayments = collect(DB::table('order_realex')->whereIn('order_id', $orderIds)->get())
        ->keyBy('order_id')
        ->map(fn($payment) => (array)$payment);

    $paymentDataByOrder = [];
    foreach ($orderIds as $orderId) {
        if (isset($protxPayments[$orderId])) {
            $paymentDataByOrder[$orderId] = ['type' => 'protx', 'data' => $protxPayments[$orderId]];
            continue;
        }
        if (isset($evopayPayments[$orderId])) {
            $paymentDataByOrder[$orderId] = ['type' => 'evopay', 'data' => $evopayPayments[$orderId]];
            continue;
        }
        if (isset($paypalPayments[$orderId])) {
            $paymentDataByOrder[$orderId] = ['type' => 'paypal', 'data' => $paypalPayments[$orderId]];
            continue;
        }
        if (isset($worldpayPayments[$orderId])) {
            $paymentDataByOrder[$orderId] = ['type' => 'worldpay', 'data' => $worldpayPayments[$orderId]];
            continue;
        }
        if (isset($stripePayments[$orderId])) {
            $paymentDataByOrder[$orderId] = ['type' => 'stripe', 'data' => $stripePayments[$orderId]];
            continue;
        }
        if (isset($realexPayments[$orderId])) {
            $paymentDataByOrder[$orderId] = ['type' => 'realex', 'data' => $realexPayments[$orderId]];
        }
    }
}

require_once $path . 'core/admin/templates/adminheader.html.php';
?>

    <ul class="breadcrumbs el">
        <li><a href="/admin/">Home</a> /</li>
        <li class="end">Orders</li>
    </ul>

    <h1>Orders</h1>

<?php

include __DIR__ . '/tabnav.php';

if (!empty($errors)) {
    showerrors($errors);
}
if (!empty($warnings)) {
    showerrors($warnings, 'alert');
}

if (!empty($_SESSION['error'])) {
    echo showmsg($_SESSION['error'], 'error', $path);
    unset($_SESSION['error']);
}

if (!empty($_SESSION['message'])) {
    echo showmsg($_SESSION['message'], 'success', $path);
    unset($_SESSION['message']);
}

include_once __DIR__ . '/templates/manage_orders_filter.php';

if ($totalnumrows > 0) {
    ?>
    <form action="<?= \Util::add_query_arg([]) ?>" method="post" id="orderBulkActionForm">

        <div class="row alignRight">
            <div class="row">
                <strong>Orders Found:</strong>
                <?= $totalnumrows ?>
            </div>

            <?php
            include($path . 'core/admin/templates/per_page.php');
            include($path . 'core/admin/templates/pagination.php');

            foreach ($_GET as $key => $value) {
                if (in_array($key, ViewHelper::$view_state_keys) && $value !== '') {
                    ?>
                    <input type="hidden" name="<?= clean_page($key) ?>" value="<?= clean_page($value) ?>"/>
                    <?php
                }
            }
            ?>

            <?php if (Permissions::can(Permissions::DO_BULK_ORDER_ACTIONS)) { ?>
                <div class="right">
                    <input name="workstation_number" type="number" placeholder="Workstation number" width="100" />
                    <select name="processing_action"
                            class="INPUT1"
                            id="processing_action"
                    >
                        <option value="0">--- ACTION ---</option>
                        <option value="ship">Mark Shipped</option>
                        <option value="create_shipments">Create Shipments</option>
                        <option value="print_prescriptions" data-new-window="true">Print Prescriptions</option>
                        <option value="print_invoices" data-new-window="true">Print Invoices</option>
                        <option value="print_pharmacy_labels" data-new-window="true">Print Pharmacy Labels</option>
                        <?php
                        if (defined("TRUSTPILOT_API_ENABLED") && TRUSTPILOT_API_ENABLED) {
                            ?>
                            <option value="trustpilot">Send Trustpilot invitations</option>
                            <?php
                        }
                        ?>
                    </select>
                    <select name="bulk_status_change"
                            class="INPUT1"
                            id="bulk_status_change">
                        <option value="">--- STATUS ---</option>
                        <?php
                        foreach (\Mtc\Shop\Order::$statuses as $key => $status) {
                            echo "<option value='{$key}'>{$status}</option>";
                        }
                        ?>
                    </select>

                    <input name="action" type="submit" value="Submit"/>
                </div>
            <?php } ?>
        </div>

        <table class="manage-items">
            <?php


            if (Permissions::can(Permissions::DOCTOR_ORDER_VIEW)) {
                ViewHelper::getHeaderRow('doctor');
            } elseif (Permissions::can(Permissions::PHARMACIST_ORDER_VIEW)) {
                ViewHelper::getHeaderRow('pharmacist');
            } else {
                ViewHelper::printCheckboxRow();
                ViewHelper::getHeaderRow('manager');
            }
            ?>
            <tbody class="tabular">
            <?php
            $flags = [
                    1 => 'brown',
                    2 => 'black',
            ];

            $orders_to_review = [];

            foreach ($orderData as $k => $data) {
                $orderId = (int)$data->id;
                $orderItems = $orderItemsByOrder->get($orderId, collect())->values();

                $order = new Order();
                $order->enableLightweightMode();
                $order->setPreloadedInfo($orderInfos->get($orderId));
                $order->setPreloadedAddresses($orderAddresses->get($orderId));
                $order->setOrderItemsCollection($orderItems);
                $order->setRefundedItemIds($refundedItemsByOrder[$orderId] ?? []);
                $order->setEloquentModel($data);
                $order->setPreloadedPaymentData($paymentDataByOrder[$orderId] ?? null);
                $order->setAdminUser($orderAdmins->get((int)$data->admin_id));
                $order->setMemberModel($data->customer ?? null);
                $order->build($data->toArray());
                $order->ordered_within_four_weeks = $orderedWithinCounts[$orderId] ?? 0;

                $orderItemModelsById = $orderItems->keyBy('id');

                if (Permissions::can(Permissions::DOCTOR_ORDER_VIEW)) {
                    ViewHelper::getDataRow('doctor', $order);
                } elseif (Permissions::can(Permissions::PHARMACIST_ORDER_VIEW)) {
                    ViewHelper::getDataRow('pharmacist', $order);
                } else {
                    ViewHelper::getDataRow('manager', $order);
                }

                if (!Permissions::isReviewer()) {
                    continue;
                }

                foreach ($order->getItems() as $order_line) {
                    /** @var OrderItem|null $orderItem */
                    $orderItem = $orderItemModelsById->get($order_line['id']);
                    if ($orderItem === null) {
                        continue;
                    }

                    $item_model = $orderItem->item;
                    if ($item_model === null) {
                        continue;
                    }
                    // User has no access right to approve
                    if (!OrderItem::orderItemCanBeApproved($orderItem)) {
                        continue;
                    }

                    // Already reviewed by doctor
                    if (Permissions::can(Permissions::REVIEW_DOCTOR_ORDERS) && (int)$order_line['approved'] !== 0) {
                        continue;
                    }

                    if (Permissions::can(Permissions::REVIEW_PHARMACY_ORDERS)) {
                        // Already clinically checked by pharmacist
                        if ($orderItem->clinic_checked) {
                            continue;
                        }

                        if ($item_model->product_type === \Mtc\Shop\Item::TYPE_PHARMACY) {
                            // Can only review pharmacy products that haven't been reviewed
                            if ((int)$orderItem->approved !== 0) {
                                continue;
                            }
                        } elseif ($item_model->product_type === \Mtc\Shop\Item::TYPE_DOCTOR) {
                            // Can only review doctor products that have been reviewed by doctor
                            if ((int)$orderItem->approved !== 1) {
                                continue;
                            }
                        }
                    }
                    $orders_to_review[] = $order->getId();
                    break;
                }

            }

            ?>
            </tbody>
        </table>
    </form>
    <?php
    if (!empty($orders_to_review)) {
        ?>
        <input type="hidden" id="ordersToReview" value="<?= implode(',', $orders_to_review); ?>"/>
        <?php
    }

    include($path . 'core/admin/templates/per_page.php');
    ?>
    <div class="row" style="margin-top: 20px;">
        <?php
        include($path . 'core/admin/templates/pagination.php');
        ?>
    </div>
    <?php

} else {
    ?>
    <p>There were no orders found matching your search criteria</p>
    <?php
}

if (EXPORT_ORDERS === true && Permissions::can(Permissions::DO_BULK_ORDER_ACTIONS)) {
    ?>
    <form action="export.orders.php?<?= http_build_query($_GET); ?>" method="post" enctype="multipart/form-data" class="style">
        <h2>Export Orders</h2>
        <p>Click export below to export the above orders as a CSV</p>

        <div class="row">
            <input type="submit" value="Export"/>
        </div>
    </form>
    <?php
    if (!empty($_SESSION['adminId']) && $_SESSION['adminId'] == 1) {
        ?>
        <form action="export.orders.php?anonymised=1&<?= http_build_query($_GET); ?>" method="post" enctype="multipart/form-data" class="style">
            <h2>Export Anonymised Order Data</h2>
            <p>Click export below to export the above orders as a CSV</p>

            <div class="row">
                <input type="submit" value="Export"/>
            </div>
        </form>
        <?php
    }
}
if (Permissions::can(Permissions::EXPORT_ELECTRONIC_PRIVATE_REGISTER)) {
    ?>
    <form action="export.orders.doctor.php" method="post" class="style">
        <h2>Export Electronic Private Register</h2>
        <p>This will export a CSV containing records of all 'Doctor Items' dispensed within the specificed date.</p>
        <input type="hidden" name="start_date" value="<?= clean_page($_REQUEST['start_date']); ?>"/>
        <input type="hidden" name="end_date" value="<?= clean_page($_REQUEST['end_date']); ?>"/>
        <div class="row">
            <input type="submit" value="Export"/>
        </div>
    </form>
    <?php
}

require_once $path . 'core/admin/templates/adminfooter.html.php';
