<?php

namespace App\Http\Controllers;

use App\Constants\Status;
use App\Lib\BusLayout;
use App\Models\AdminNotification;
use App\Models\BookedTicket;
use App\Models\Frontend;
use App\Models\Language;
use App\Models\Page;
use App\Models\Stoppage;
use App\Models\SupportMessage;
use App\Models\SupportTicket;
use App\Models\TicketPrice;
use App\Models\Trip;
use App\Models\TripRoute;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cookie;
use Illuminate\Support\Facades\Session;

class SiteController extends Controller {

    public function index() {
        $pageTitle   = 'Home';
        $sections    = Page::where('tempname', activeTemplate())->where('slug', '/')->first();
        $seoContents = $sections->seo_content;
        $seoImage    = $seoContents?->image ? getImage(getFilePath('seo') . '/' . $seoContents?->image, getFileSize('seo')) : null;
        return view('Template::home', compact('pageTitle', 'sections', 'seoContents', 'seoImage'));
    }

    public function pages($slug) {
        $page        = Page::where('tempname', activeTemplate())->where('slug', $slug)->firstOrFail();
        $pageTitle   = $page->name;
        $sections    = $page->secs;
        $seoContents = $page->seo_content;
        $seoImage    = $seoContents?->image ? getImage(getFilePath('seo') . '/' . $seoContents?->image, getFileSize('seo')) : null;
        return view('Template::pages', compact('pageTitle', 'sections', 'seoContents', 'seoImage'));
    }

    public function contact() {
        $pageTitle   = "Contact Us";
        $user        = auth()->user();
        $sections    = Page::where('tempname', activeTemplate())->where('slug', 'contact')->first();
        $seoContents = $sections->seo_content;
        $seoImage    = $seoContents?->image ? getImage(getFilePath('seo') . '/' . $seoContents?->image, getFileSize('seo')) : null;
        return view('Template::contact', compact('pageTitle', 'user', 'sections', 'seoContents', 'seoImage'));
    }

    public function contactSubmit(Request $request) {
        $request->validate([
            'name'    => 'required',
            'email'   => 'required',
            'subject' => 'required|string',
            'message' => 'required',
        ]);

        $request->session()->regenerateToken();

        if (!verifyCaptcha()) {
            $notify[] = ['error', 'Invalid captcha provided'];
            return back()->withNotify($notify);
        }

        $random = getNumber();

        $ticket           = new SupportTicket();
        $ticket->user_id  = auth()->id() ?? 0;
        $ticket->name     = $request->name;
        $ticket->email    = $request->email;
        $ticket->priority = Status::PRIORITY_MEDIUM;

        $ticket->ticket     = $random;
        $ticket->subject    = $request->subject;
        $ticket->last_reply = Carbon::now();
        $ticket->status     = Status::TICKET_OPEN;
        $ticket->save();

        $adminNotification            = new AdminNotification();
        $adminNotification->user_id   = auth()->user() ? auth()->user()->id : 0;
        $adminNotification->title     = 'A new contact message has been submitted';
        $adminNotification->click_url = urlPath('admin.ticket.view', $ticket->id);
        $adminNotification->save();

        $message                    = new SupportMessage();
        $message->support_ticket_id = $ticket->id;
        $message->message           = $request->message;
        $message->save();

        $notify[] = ['success', 'Ticket created successfully!'];

        return to_route('ticket.view', [$ticket->ticket])->withNotify($notify);
    }

    public function policyPages($slug) {
        $policy      = Frontend::where('slug', $slug)->where('data_keys', 'policy_pages.element')->firstOrFail();
        $pageTitle   = $policy->data_values->title;
        $seoContents = $policy->seo_content;
        $seoImage    = $seoContents?->image ? frontendImage('policy_pages', $seoContents->image, getFileSize('seo'), true) : null;
        return view('Template::policy', compact('policy', 'pageTitle', 'seoContents', 'seoImage'));
    }

    public function changeLanguage($lang = null) {
        $language = Language::where('code', $lang)->first();
        if (!$language) {
            $lang = 'en';
        }

        session()->put('lang', $lang);
        return back();
    }

    public function blog() {
        $pageTitle = 'Blogs';
        $blogs     = Frontend::where('data_keys', 'blog.element')->where('tempname', activeTemplateName())->orderBy('id', 'DESC')->paginate(getPaginate());
        $sections  = Page::where('tempname', activeTemplate())->where('slug', 'blog')->first();
        return view('Template::blog', compact('pageTitle', 'blogs', 'sections'));
    }

    public function blogDetails($slug) {
        $blog        = Frontend::where('slug', $slug)->where('data_keys', 'blog.element')->firstOrFail();
        $latestPost  = Frontend::where('data_keys', 'blog.element')->where('slug', '!=', $slug)->orderBy('id', 'desc')->take(10)->get();
        $pageTitle   = 'Blog Details';
        $seoContents = $blog->seo_content;
        $seoImage    = $seoContents?->image ? frontendImage('blog', $seoContents->image, getFileSize('seo'), true) : null;
        return view('Template::blog_details', compact('blog', 'pageTitle', 'seoContents', 'latestPost', 'seoImage'));
    }

    public function cookieAccept() {
        Cookie::queue('gdpr_cookie', gs('site_name'), 43200);
    }

    public function cookiePolicy() {
        $cookieContent = Frontend::where('data_keys', 'cookie.data')->first();
        abort_if($cookieContent->data_values->status != Status::ENABLE, 404);
        $pageTitle = 'Cookie Policy';
        $cookie    = Frontend::where('data_keys', 'cookie.data')->first();
        return view('Template::cookie', compact('pageTitle', 'cookie'));
    }

    public function ticket() {
        $pageTitle    = 'Book Ticket';
        $emptyMessage = 'There is no trip available';
        $data         = $this->getTripsData(request());
        $trips        = $data['trips'];

        if ($data['errors']) {
            return back()->withNotify($data['errors']);
        }

        $routes    = TripRoute::active()->get();
        $stoppages = Stoppage::with('city')->active()->get();
        return view('Template::ticket', compact('pageTitle', 'trips', 'routes', 'emptyMessage', 'stoppages'));
    }

    public function tripSeatDetails() {
        $pickup      = request()->pickup;
        $destination = request()->destination;
        $id          = decrypt(request()->id);
        $trip        = Trip::availableTrip()->with(['fleetType', 'route', 'startFrom', 'endTo'])->where('id', $id)->first();

        if(!$trip) {
            $data = ['error' => 'Trip not found!'];
            return response()->json($data);
        }
        $route       = $trip->route;
        $stoppagesIds = $trip?->route?->stoppages;
        $stoppages   = Stoppage::with('city')->routeStoppages($stoppagesIds);
        $busLayout   = new BusLayout($trip);
        $price       = '';
        $sdArray     = [(string) $pickup , (string) $destination];
        $ticketPrice = TicketPrice::with('prices')->where('vehicle_route_id', $trip->vehicle_route_id)->where('fleet_type_id', $trip->fleet_type_id)->with('route')->first();
        $price = $ticketPrice->prices()->where(function ($query) use ($sdArray) {
            $query->whereJsonContains('source_destination', $sdArray);
        })->where('ticket_price_id', $ticketPrice->id)->first()->price ?? 0;
        $bookedTicket = BookedTicket::bookedAndPending()->with('user', 'guestUser')->where('trip_id', $trip->id)->get()->toArray();
        $html = view('Template::partials.seat_layout', compact('trip', 'busLayout',  'price', 'pickup', 'destination'))->render();
        return response()->json([
            'html' => $html,
            'bookedSeats'    => $bookedTicket,
            'reqSource'      => $pickup,
            'reqDestination' => $destination,
            'stoppagesIds'      => $stoppagesIds,
            'stoppages'      => $stoppages,
            'price'          => $price,
        ]);
    }


    public function getTicketPrice(Request $request) {
        $ticketPrice    = TicketPrice::where('vehicle_route_id', $request->vehicle_route_id)->where('fleet_type_id', $request->fleet_type_id)->with('route')->first();
        $stoppages      = $ticketPrice?->route->stoppages;
        $sourcePos      = array_search($request->source_id, $stoppages);
        $destinationPos = array_search($request->destination_id, $stoppages);

        $bookedTicket = BookedTicket::bookedAndPending()->with('user', 'guestUser')->where('trip_id', $request->trip_id)->where('date_of_journey', Carbon::parse($request->date)->format('Y-m-d'))->get()->toArray();
        if ($sourcePos >= $destinationPos) {
            $data = [
                'error' => 'Select Pickup Point & Dropping Point Properly',
            ];
            return response()->json($data);
        }
        $sdArray  = [$request->source_id, $request->destination_id];
        $getPrice = $ticketPrice->prices()->where('source_destination', json_encode($sdArray))
            ->first();

        if ($getPrice) {
            $price = $getPrice->price;
        } else {
            $price = [
                'error' => 'Admin may not set prices for this route. So, you can\'t buy ticket for this trip.',
            ];
        }
        $data['bookedSeats']    = $bookedTicket;
        $data['reqSource']      = $request->source_id;
        $data['reqDestination'] = $request->destination_id;
        $data['stoppages']      = $stoppages;
        $data['price']          = $price;
        return response()->json($data);
    }

    public function bookTicket(Request $request, $id) {

        if (!auth()->user()) {
            $notify[] = ['error', 'You must login to book tickets'];
            return redirect()->route('user.login')->withNotify($notify);
        }

        $request->validate([
            "pickup_point"    => "required|integer|exists:stoppages,id",
            "dropping_point"  => "required|integer|exists:stoppages,id",
            "boarding_point"  => "required|integer|exists:stoppages,id",
            "date_of_journey" => "required|date",
            "seats"           => "required|string",
        ], [
            "seats.required" => "Please select at least one seat",
        ]);

        $date_of_journey = Carbon::parse($request->date_of_journey);
        $today           = Carbon::today()->format('Y-m-d');

        if ($date_of_journey->format('Y-m-d') < $today) {
            $notify[] = ['error', 'Date of journey cant\'t be less than today'];
            return redirect()->back()->withNotify($notify);
        }

        $trip            = Trip::availableTrip()->findOrFail($id);
        $route           = $trip->route;
        $stoppages       = $trip?->route->stoppages;
        $source_pos      = array_search($request->pickup_point, $stoppages);
        $destination_pos = array_search($request->dropping_point, $stoppages);

        $booked_ticket = BookedTicket::where('trip_id', $id)
            ->where('date_of_journey', $date_of_journey->format('Y-m-d'))
            ->where('pickup_point', $request->pickup_point)
            ->where('dropping_point', $request->dropping_point)
            ->bookedAndPending()
            ->where(function ($query) use ($request) {
                foreach (explode(',', $request->seats) as $seat) {
                    $query->orWhereJsonContains('seats', $seat);
                }
            })
            ->get();

        if ($booked_ticket->count() > 0) {
            $notify[] = ['error', 'Those seats are already booked'];
            return redirect()->back()->withNotify($notify);
        }

        if ($source_pos >= $destination_pos) {
            $notify[] = ['error', 'Select Pickup Point & Dropping Point Properly'];
            return redirect()->back()->withNotify($notify);
        }

        $ticketPrice = TicketPrice::where('fleet_type_id', $trip->fleetType->id)->where('vehicle_route_id', $route->id)->first();
        $sdArray     = [$request->pickup_point, $request->dropping_point];

        $getPrice = $ticketPrice->prices()
            ->where('source_destination', json_encode($sdArray))
            ->first();
        if (!$getPrice) {
            $notify[] = ['error', 'Invalid selection'];
            return back()->withNotify($notify);
        }

        $seats                            = array_filter((explode(',', $request->seats)));
        $unitPrice                        = getAmount($getPrice->price);
        $pnr_number                       = getTrx(10);
        $bookedTicket                     = new BookedTicket();
        $bookedTicket->user_id            = auth()->id();
        $bookedTicket->trip_id            = $trip->id;
        $bookedTicket->source_destination = [$request->pickup_point, $request->dropping_point];
        $bookedTicket->pickup_point       = $request->pickup_point;
        $bookedTicket->dropping_point     = $request->dropping_point;
        $bookedTicket->boarding_point     = $request->boarding_point;
        $bookedTicket->seats              = $seats;
        $bookedTicket->ticket_count       = sizeof($seats);
        $bookedTicket->unit_price         = $unitPrice;
        $bookedTicket->sub_total          = sizeof($seats) * $unitPrice;
        $bookedTicket->date_of_journey    = Carbon::parse($request->date_of_journey)->format('Y-m-d');
        $bookedTicket->pnr_number         = $pnr_number;
        $bookedTicket->status             = Status::BOOKED_PENDING;
        $bookedTicket->last_payment_attempt_at = now();
        $bookedTicket->save();
        session()->put('pnr_number', $pnr_number);
        return redirect()->route('user.deposit.index');
    }

    public function ticketSearch(Request $request) {
        $emptyMessage = 'There is no trip available';
        $data         = $this->getTripsData($request);
        $trips        = $data['trips'];
        if ($data['errors']) {
            return response()->json(['error' => $data['errors']]);
        }
        $html = view('Template::partials.trips', compact('trips', 'emptyMessage'))->render();
        return response()->json(['html' => $html]);
    }

    private function getTripsData($request) {
        $data['errors'] = false;
        $data['trips']  = [];
        $notify        = [];
        $dateOfJourney = Carbon::parse($request->date_of_journey)->format('Y-m-d');
        if ($request->pickup && $request->destination && $request->pickup == $request->destination) {
            $notify[] = ['error', 'Please select pickup point and destination point properly'];
        }
        if ($dateOfJourney < Carbon::now()->format('Y-m-d')) {
            $notify[] = ['error', 'Date of journey can\'t be less than today.'];
        }
        if ($notify) {
            $data['errors'] = $notify;
            return $data;
        }
        $sourceDestination = NULL;
        $tripQuery = Trip::availableTrip()->initiated()->whereDate('start_time', $dateOfJourney)->where('start_time', '>', now());
        if ($request->pickup && $request->destination) {
            $pickup      = $request->pickup;
            $destination = $request->destination;

            $tripQuery->whereHas('route', function ($query) use ($pickup, $destination) {
                $query->whereRaw("JSON_CONTAINS(stoppages, JSON_QUOTE(?))", [(string) $pickup])
                    ->whereRaw("JSON_CONTAINS(stoppages, JSON_QUOTE(?))", [(string) $destination])
                    ->whereRaw("JSON_SEARCH(stoppages, 'one', ?) < JSON_SEARCH(stoppages, 'one', ?)", [(string) $pickup, (string) $destination]);
            });
            $sourceDestination = [$pickup, $destination];
        }

        $tripQuery->select('trips.*',
            'ticket_prices.id as ticket_price_id',
            'ticket_prices.vehicle_route_id as ticket_vehicle_route_id',
            'ticket_prices.fleet_type_id as ticket_fleet_type_id',
            'ticket_prices.price as base_price'
        )->join('ticket_prices', function ($join) {
            $join->on('ticket_prices.fleet_type_id', '=', 'trips.fleet_type_id')
                ->on('ticket_prices.vehicle_route_id', '=', 'trips.vehicle_route_id');
        });

        if (!empty($sourceDestination)) {
            $tripQuery->addSelect(
                'ticket_price_by_stoppages.source_destination',
                'ticket_price_by_stoppages.price as stoppage_price'
            )
            ->join('ticket_price_by_stoppages', 'ticket_price_by_stoppages.ticket_price_id', '=', 'ticket_prices.id')
            ->whereJsonContains('ticket_price_by_stoppages.source_destination', $sourceDestination)
            ->when($request->order, function ($query) use ($request) {
                $query->orderBy('stoppage_price', $request->order);
            });
        } else {
            $tripQuery->when($request->order, function ($query) use ($request) {
                $query->orderBy('base_price', $request->order);
            });
        }

        $data['trips'] = $tripQuery->with(['fleetType', 'route', 'startFrom', 'endTo'])->paginate(getPaginate());

        Session::flash('pickup', $request->pickup);
        Session::flash('destination', $request->destination);
        Session::flash('date_of_journey', Carbon::parse($request->date_of_journey)->format('m/d/Y'));
        return $data;
    }

    public function placeholderImage($size = null) {
        $imgWidth  = explode('x', $size)[0];
        $imgHeight = explode('x', $size)[1];
        $text      = $imgWidth . '×' . $imgHeight;
        $fontFile  = realpath('assets/font/solaimanLipi_bold.ttf');
        $fontSize  = round(($imgWidth - 50) / 8);
        if ($fontSize <= 9) {
            $fontSize = 9;
        }
        if ($imgHeight < 100 && $fontSize > 30) {
            $fontSize = 30;
        }

        $image     = imagecreatetruecolor($imgWidth, $imgHeight);
        $colorFill = imagecolorallocate($image, 100, 100, 100);
        $bgFill    = imagecolorallocate($image, 255, 255, 255);
        imagefill($image, 0, 0, $bgFill);
        $textBox    = imagettfbbox($fontSize, 0, $fontFile, $text);
        $textWidth  = abs($textBox[4] - $textBox[0]);
        $textHeight = abs($textBox[5] - $textBox[1]);
        $textX      = ($imgWidth - $textWidth) / 2;
        $textY      = ($imgHeight + $textHeight) / 2;
        header('Content-Type: image/jpeg');
        imagettftext($image, $fontSize, 0, $textX, $textY, $colorFill, $fontFile, $text);
        imagejpeg($image);
        imagedestroy($image);
    }

    public function maintenance() {
        $pageTitle = 'Maintenance Mode';
        if (gs('maintenance_mode') == Status::DISABLE) {
            return to_route('home');
        }
        $maintenance = Frontend::where('data_keys', 'maintenance.data')->first();
        return view('Template::maintenance', compact('pageTitle', 'maintenance'));
    }
}
