<?php

namespace App\Http\Controllers;

use App\Http\Controllers\PaymentController;
use App\Http\Requests\ChangeOrderStatusRequest;
use App\Models\User;
use Carbon\Carbon;
use App\Http\Requests\CheckHourlyAvailabilityRequest;
use App\Http\Requests\SubmitOrderRequest;
use App\Models\Order;
use App\Models\Service;
use App\Models\Payment;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Http;

class OrdersController extends PaymentController
{
    /**
     * @var bool
     */
    private bool $api_call;
    /**
     * @brief assign true|false to $api_call by checking if the request is an API call
     *
     * @return void
     */
    public function __construct() {
        $this->api_call = request()->header('Accept') == "application/json";
    }
    /**
     * @brief fetching the required data and respond properly
     *
     * @return \Illuminate\Contracts\View\View|string
     */
    public function index() {
        // check user type (admin | user)
        if (isAdmin()) {
            // throw a 403 error if the request is an API call
            if ($this->api_call) {
                auth()->logout();
                throwForbiddenError(['errors' => ['Admin is forbidden to access this content via the API']]);
                exit();
            }
            $path = "admin-pages";
            $orders = Order::join('services', 'orders.sid', '=', 'services.id')
                           ->join('users', 'orders.uid', '=', 'users.id')
                           ->select('orders.*', 'services.title', 'services.hourly_rate', 'services.daily_rate', 'users.firstname', 'users.lastname', 'users.idnum')
                           ->orderBy('orders.placement_datetime', 'desc')
                           ->get();
        } else {
            $path = "customer-pages";
            $orders = Order::join('services', 'orders.sid', '=', 'services.id')
                           ->join('users', 'orders.uid', '=', 'users.id')
                           ->select('orders.*', 'services.title', 'services.hourly_rate', 'services.daily_rate')
                           ->where('orders.uid', auth()->user()->id)
                           ->orderBy('orders.placement_datetime', 'desc')
                           ->get();
        }
        // return based on request type (web | api)
        return !$this->api_call ?
            view("v1/02_dash-template/$path/orders", ['orders'=>$orders]) :
            successfulResponse(['orders'=>$orders]);
    }
    /**
     * @brief fetch the mentioned service details
     *
     * @param id
     *
     * @return \Illuminate\Contracts\View\View|string
     */
    public function viewOrderService () {
        $service = Service::find(request('id'));
        if ($service) {
            $sids = request('id');
            $ids = $this->getRelatedService(request('id'));
            foreach ($ids as $id)
                $sids .= $id != request('id') ? ", ".$id : null;

            $allOrders = DB::select( "
                SELECT `date`, `from`, `until`
                FROM orders
                WHERE (`date` >= :date OR `from` >= :from OR `until` <= :until)
                  AND (`status` = 'done' OR (`status` = 'reserved' AND `placement_datetime` > :pd))
                  AND sid IN ($sids)
            ", ['date'=>date('Y-m-d'), 'from'=>date('Y-m-d'), 'until'=>date('Y-m-d', strtotime("+60 days")), 'pd'=>date('Y-m-d H:i:s', strtotime("-15 minutes"))]);
//            return $allOrders;
            $ao = []; // all orders
            $do = []; // daily orders
            $ho = []; // hourly orders
            foreach ($allOrders as $order) {
                if (!is_null($order->from)) {
                    $startDate = Carbon::parse($order->from);
                    $endDate = Carbon::parse($order->until);
                    $dateRange = [];
                    while ($startDate <= $endDate) {
                        $dateRange[] = gregorianToJalali($startDate->toDateString(), "Y/m/d", "en");
                        $startDate->addDay();
                    }
                } else {
                    $dateRange = [gregorianToJalali($order->date, "Y/m/d", "en")];
                    $ho[] = $dateRange[0];
                }
                $ao = array_merge($ao, $dateRange);
            }
            $ao = array_unique($ao);
            $ho = array_unique($ho);
            $do = array_diff($ao, $ho);
//            return $do;
            return !$this->api_call ?
                view('v1/02_dash-template/customer-pages/service-single', ['service'=>$service, 'do'=>$do, 'ao'=>$ao]) :
                successfulResponse(['service'=>$service, 'do'=>$do, 'ao'=>$ao]);
        } else
            !$this->api_call ? abort(404) : throwNotFoundError();
    }

    /**
     * @brief check for available hours for the requested service id
     *
     * @param CheckHourlyAvailabilityRequest $request
     *
     * @return false|string
     */
    public function checkHourlyAvailability (CheckHourlyAvailabilityRequest $request) {
        $sids = request('sid');
        $ids  = $this->getRelatedService(request('sid'));
        foreach ($ids as $id)
            $sids .= $id != request('id') ? ", ".$id : null;

        $date = jalaliToGregorian(request('date'));
        $hourlyOrders = DB::select( "
            SELECT `hours`
            FROM orders
            WHERE `date` = :date
              AND (`status` = 'done' OR (`status` = 'reserved' AND `placement_datetime` > :pd))
              AND sid IN ($sids)
        ", ['date'=>$date, 'pd'=>date('Y-m-d H:i:s', strtotime("-15 minutes"))]);

        $hours = [];
        if ($date == date('Y-m-d'))
            for ($i = 0; $i <= date('H'); $i++)
                $hours[] = $i;

        if (count($hourlyOrders) > 0)
            foreach (json_decode($hourlyOrders[0]->hours, true) as $hour)
                $hours[] = $hour;

        $hours = array_unique($hours);
        return !$this->api_call ? json_encode($hours) : successfulResponse(['booked-hours'=>$hours]);
    }

    public function viewOrderHistory () {

    }

    public function submitOrder (SubmitOrderRequest $request) {
//        return $request;
        $save = true;
        $sids = $this->getRelatedService($request->sid);
        if ($request->type === "hourly") {
            $date = jalaliToGregorian($request->date);
            $rows = Order::where( 'date', '=', $date )->whereIn( 'sid', $sids )->get();
            if ( count( $rows ) > 0 ) {
                foreach ( $rows as $row ) {
                    $otimes = json_decode( $rows['hours'], true );
                    foreach ( $request->times as $time ) {
                        foreach ( $otimes as $ot ) {
                            if ( $ot == $time ) {
                                $save = false;
                            }
                        }
                    }
                }
            }
            if ( $save ) {
//                    echo count($request->times);
                $order                     = new Order;
                $order->uid                = auth()->user()->id;
                $order->sid                = $request->sid;
                $order->hours              = json_encode( $request->times, JSON_UNESCAPED_UNICODE );
                $order->date               = $date;
                $order->status             = 'reserved';
                $order->placement_datetime = date( 'Y-m-d H:i:s' );
                $order->save();
                // it's time to process payment
                $payable_amount = $request->pph;

                // first we check if the user has the sufficient credit/coupon to pay with
                $user_credit = auth()->user()->credit;
                if ($user_credit > 0) {
                    if ($user_credit >= $payable_amount) {
                        // create an invoice for this order
                        PaymentController::createInvoice($order->id, $payable_amount, 'credit');
                        if (self::processOrder($order->id, 200)) {
                            // update payment status
                            PaymentController::updatePaymentStatus('successful');
                            // let's show the proper message to the user
                            return 'order unsuccessful';
//                                return back()->with('msg', 'سرویس با موفقیت خریداری گردید');
                        } else {
                            // update payment status
                            PaymentController::updatePaymentStatus('failed');
                            // return the proper response
                            return 'order unsuccessful';
//                                return back()->with('msg', 'فعالسازی سرویس با خطا مواجه شد، تیم پشتیبانی در حال بررسی این موضوع می‌باشد');
                        }
                    } else {
                        // creating proper invoices for this order and transferring the user to the ipg
                        if ($user_credit > 0) {
                            PaymentController::createInvoice( $order->id, $user_credit, 'credit' );
                            $payable_amount -= $user_credit;
                        }
                    }
                }
//                    PaymentController::createInvoice($order->id, $payable_amount, 'ipg');
            } else {
                return back()->with( 'msg', 'در این بازه زمانی، سرویس رزرو می‌باشد' );
            }
        } elseif ($request->type === "daily") {
            $from  = jalaliToGregorian($request->from);
            $until = jalaliToGregorian($request->until);
            $rows = Order::where('from', '<=', date('Y-m-d'))->orWhere('from', '<=', $from)->orWhere('until', '>=', $until)->orWhere('date', $from)->orWhere('date', $until)->whereIn('sid', $sids)->get();
            if (count($rows) > 0)
                return back()->with('msg', 'در این بازه زمانی، سرویس رزرو می‌باشد');
            else {
                $order = new Order;
                $order->uid = auth()->user()->id;
                $order->sid = $request->sid;
                $order->from = $from;
                $order->until = $until;
                $order->status = 'reserved';
                $order->placement_datetime = date('Y-m-d H:i:s');
                $order->save();
//                return redirect()->route('dummy-payment.view', ['oid'=>$order->id]);
//                return back()->with('msg', 'سرویس با موفقیت خریداری گردید');
            }
        }
    }

    /**
     * @param ChangeOrderStatusRequest $request
     * @brief change order status manually
     *
     * @return \Illuminate\Http\RedirectResponse
     */
    public function changeOrderStatus (ChangeOrderStatusRequest $request) {
        // update order status upon request
        $order = Order::find($request->oid);
        $order->status = $request->action;
        $order->save();
        // if order has been manually set to `done`
        if ($request->action === 'done')
            OrdersController::processOrder($request->oid, '200');

        return back()->with('msg', 'تغییرات با موفقیت اعمال شد');
    }

    /**
     * @param $oid
     * @param $status
     *
     * @brief update order status based on payment status
     *
     * @return bool
     */
    public static function processOrder ($oid, $status): bool {
        $order = Order::find($oid);
        $result = false;
        switch ($status) {
            // payment successful
            case 200:
                // update order status, then get the `mid` of the service
                $service = Service::find($order->oid);
                // create a new machine
                $response = MachineController::createMachine($order->oid);
                // check the response, if 201, then update it to done
                if ($response->created()) {
                    $result = true;

                    $rspObject     = json_decode($response->body(), true);
                    $order->vmid   = $rspObject['machineId'];
                    $order->status = 'done';
                    $order->save();
                    // change payment status to successful
                    PaymentController::updatePaymentStatus('successful',null, $oid);
                }
                // create an internal ticket to inform an operator of the issue, if the response code ain't 201
                elseif ($response->badRequest()) {

                } elseif ($response->serverError()) {

                } else {

                }
                break;
            // payment failed
            case 500:
                $order->status = 'cancelled';
                $order->save();
                break;
        }
        return $result;
    }

    /**
     * @param $id
     * @brief helper function, to check the availability of parent/children of a service
     *
     * @return array
     */
    private function getRelatedService ($id) : array {
        $sids = [$id];
        $services = Service::select('id', 'hourly_rate', 'daily_rate')->where('parent', 'like', '%"id":'.$id.',%')->orWhere('children', 'like', '%"id":'.$id.',%')->get();
        foreach ($services as $service)
            $sids[] = $service['id'];
        return $sids;
    }
}
