<?php
/**
 * @author Ulrich Hecht <ulrich.hecht@hecht-software.de>
 * @date 12/08/2016
 */

namespace AppBundle\Controller;


use AppBundle\Entity\BookingRequest;
use AppBundle\Entity\BreadcrumbEntry;
use AppBundle\Entity\Page;
use AppBundle\Entity\TravelDate;
use AppBundle\Entity\Traveler;
use AppBundle\Entity\TravelPeriodPrice;
use AppBundle\Entity\TravelPeriodPriceType;
use AppBundle\Form\BookingRequestType;
use AppBundle\Util;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class BookingController extends Controller
{
    /** @var TravelPeriodPriceType[] $priceTypeById */
    private $priceTypeById;

    /**
     * The routing for this action is entirely controlled by KernelControllerListener!
     *
     * @param Page $travelProgramPage
     * @param Request $request
     * @param $action
     *
     * @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
     * @throws \Exception
     */
    public function indexAction(Page $travelProgramPage, $action, Request $request)
    {
        $travelProgram = $travelProgramPage->getTravelProgram();
        if (!$request->query->has('nr'))
        {
            return $this->redirect($travelProgramPage->getUrlPath());
        }
        $this->getDoctrine()->getRepository('AppBundle:TravelPeriod')->getTrueTravelPeriods($travelProgram);

        $this->priceTypeById = $this->getDoctrine()->getRepository('AppBundle:TravelPeriodPriceType')->findAllIndexedById();

        // #TODO Consider changing key of travel dates
        foreach ($travelProgram->getTravelDates() as $curTravelDate)
        {
            if ($curTravelDate->getName() == $request->query->get('nr'))
            {
                $travelDate = $curTravelDate;
                break;
            }
        }
        if (!isset($travelDate))
        {
            throw $this->createNotFoundException();
        }

        /** @var BookingRequest $bookingRequest */
        $bookingRequest = new BookingRequest();
        if ($request->getMethod() != 'POST')
        {
            $bookingRequest->setTravelerCount(2);
            $bookingRequest->setDeparture($travelDate->getDepartures()[0]);
        }
        $form = $this->createForm(BookingRequestType::class, $bookingRequest, [
            'travel_date' => $travelDate,
            'travel_program' => $travelProgram
        ]);
        if ($request->getMethod() == 'POST')
        {
            $form->handleRequest($request);
            $bookingRequest = $form->getData();
        }
        $htmlSummary = [];
        $bookingPriceInfo = [];
        $totalPrice = $this->calculatePrice($travelDate, $bookingRequest, $htmlSummary, $bookingPriceInfo);

        if ($action == '/buchen')
        {
            $breadcrumbEntries = Util::createBreadcrumb($travelProgramPage);
            $breadcrumbEntries[] = new BreadcrumbEntry('Buchen');

            if ($request->getMethod() == 'POST') // && $form->isValid())
            {

                $booking = $this->getDoctrine()->getRepository('AppBundle:TravelBooking')->createFromBookingRequest(
                    $bookingRequest, $travelDate, $bookingPriceInfo);
                $em = $this->getDoctrine()->getManager();
                $em->persist($booking);
                $em->flush();
               

                $crmBookingUrl = $this->get('app.booking_exporter')->process($bookingRequest, $travelDate, $bookingPriceInfo);
                $crmBookingUrl = preg_replace('/\\/api/', '', $crmBookingUrl) .'/edit';

                $this->get('mailer')->send(\Swift_Message::newInstance()
                    ->setSubject('Ihr Buchungsauftrag bei STERN TOURS')
                    ->setFrom('stern@stern-tours.de', 'STERN TOURS')
                    ->setTo($bookingRequest->getEmail())
                    ->setBody(
                        $this->renderView('default/email/bookingConfirmationEmail.txt.twig', [
                            'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..').DIRECTORY_SEPARATOR,
                            'booking_request' => $bookingRequest,
                            'booking_price_info' => $bookingPriceInfo,
                            'travel_date' => $travelDate,
                            'breadcrumb_entries' => $breadcrumbEntries,
                        ]),
                        'text/plain', 'utf-8'
                    )
                );

                $this->get('mailer')->send(\Swift_Message::newInstance()
                    ->setSubject('BUCHUNG: '. $travelProgram->getTitle() .'('. $travelDate->getName() .')')
                    ->setFrom('stern@stern-tours.de', 'STERN TOURS')
                    ->setTo('stern@stern-tours.de')
                    ->setBody(
                        $this->renderView('default/email/bookingServiceEmail.txt.twig', [
                            'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..').DIRECTORY_SEPARATOR,
                            'crm_url' => $crmBookingUrl,
                            'travel_program_url' => Util::getBaseUrl() . $travelProgramPage->getUrlPath(),
                            'booking_request' => $bookingRequest,
                            'booking_price_info' => $bookingPriceInfo,
                            'travel_date' => $travelDate,
                            'breadcrumb_entries' => $breadcrumbEntries,
                        ]),
                        'text/plain', 'utf-8'
                    )
                );
               

                // #TODO This will lead to multiple bookings due to multiple form submission. Redirect instead!
                return $this->render('default/pages/bookingConfirmation.html.twig', [
                    'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..').DIRECTORY_SEPARATOR,
                    'page' => $travelProgramPage,
                    'breadcrumb_entries' => $breadcrumbEntries,
                    'booking' => $booking,
                    'travel_program' => $travelProgram,
                    'summary' => $htmlSummary,
                    'total_price' => $totalPrice,
                ]);
            }

            return $this->render('default/pages/booking.html.twig', [
                'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..').DIRECTORY_SEPARATOR,
                'page' => $travelProgramPage,
                'travel_program' => $travelProgram,
                'travel_date' => $travelDate,
                'form' => $form->createView(),
                'price_type_by_id' => $this->priceTypeById,
                'breadcrumb_entries' => $breadcrumbEntries,
                'summary' => $htmlSummary,
                'total_price' => $totalPrice,
                'mediator_terms_filename' => $travelProgram->getIsMediated()
                    ? $this->getDoctrine()->getRepository('AppBundle:TravelOrganizer')->find(1)->getFileName()
                    : null
            ]);
        }
        elseif ($action == '/berechne-gesamtpreis')
        {
            return $this->render('default/components/booking/summary.html.twig', [
                'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..').DIRECTORY_SEPARATOR,
                'summary' => $htmlSummary,
                'total_price' => $totalPrice
            ]);
        }
        throw new \Exception('Unknow BookingController action: '. $action);
    }

    public function calculatePrice(TravelDate $travelDate, BookingRequest $bookingRequest, &$outHtmlSummary = null,
        &$outPriceInfo = null)
    {
        $ret = 0;
        $insuranceAssessmentBasis = 0;
        $travelerCount = $bookingRequest->getTravelerCount();
        if (isset($outHtmlSummary))
        {
            $insuranceHtmlSummary = [];
        }
        if (isset($outPriceInfo))
        {
            $outPriceInfo['rooms'] = [];
            $outPriceInfo['insurances'] = [];
            $outPriceInfo['options'] = [];
            $outPriceInfo['classOptions'] = [];
        }

        $departure = Util\DepartureUtil::limitIndividualArrivalPrice($bookingRequest->getDeparture(),
            $travelDate->getFlightPrice());
        if (isset($outPriceInfo))
        {
            $outPriceInfo['departure'] = $departure;
        }

        if ($departure->getExtraCharge() != 0)
        {
            $insuranceAssessmentBasis += $departure->getExtraCharge();
            $a = $travelerCount * $departure->getExtraCharge();
            $ret += $a;
            if (isset($outHtmlSummary))
            {
                $outHtmlSummary[] = [
                    'value' => $a,
                    'label' => $travelerCount .'x '. ($departure->getExtraCharge() > 0 ? 'Aufschlag' : 'Abzug') .
                        ' für Abfahrts-/Abflugort "'. $departure->getName() .'" <strong>'.
                        Util::formatPrice($departure->getExtraCharge()) .'</strong>'
                ];
            }
        }

        foreach ($bookingRequest->getTravelOptions() as $travelOption)
        {
            $insuranceAssessmentBasis += $travelOption->getPrice();
            $a = $travelerCount * $travelOption->getPrice();
            $ret += $a;
            if (isset($outHtmlSummary))
            {
                $outHtmlSummary[] = [
                    'value' => $a,
                    'label' => $travelerCount .'x zugebuchte Leistung: '. $travelOption->getName() .' <strong>'.
                        Util::formatPrice($travelOption->getPrice()) .'</strong>'
                ];
            }
            if (isset($outPriceInfo))
            {
                $outPriceInfo['options'][] = $travelOption;
            }
        }

        $persons = [
            'total' => $travelerCount,
            'adults' => $travelerCount,
            'children' => 0
        ];
        $possibleRooms = $this->searchRooms($travelDate->getPrices(), $persons);

        if (empty($possibleRooms))
        {
            if ($travelerCount % 2 == 0)
            {
                $possibleRooms = $this->splitIntoTwoGroups($travelDate->getPrices(), $persons, 'equal');
            }
            elseif ($travelerCount >= 3)
            {
                $possibleRooms = $this->splitIntoTwoGroups($travelDate->getPrices(), $persons, 'move_without_children');
            }
        }

        if ($bookingRequest->getComfort())
        {
            foreach ($possibleRooms as $room)
            {
                $insuranceAssessmentBasis += $room['price']->getEffectiveComfortPrice();
                $a = $room['persons']['total'] * $room['price']->getEffectiveComfortPrice();
                $ret += $a;
                if (isset($outHtmlSummary))
                {
                    $outHtmlSummary[] = [
                        'value' => $a,
                        'label' => $room['persons']['total'] .'x zugebuchte Leistung: Komfort-Kategorie <strong>'.
                            Util::formatPrice($room['price']->getEffectiveComfortPrice()) .'</strong>'
                    ];
                }
                if (isset($outPriceInfo))
                {
                    $outPriceInfo['classOptions'][] = [
                        'count' => $room['persons']['total'],
                        'name' => 'zugebuchte Leistung: Komfort (4 Sterne)',
                        'price' => $room['price']->getEffectiveComfortPrice()
                    ];
                }
            }
        }

        $insuranceTotal = 0;

        foreach ($possibleRooms as $room)
        {
            $adultCount = $room['persons']['adults'];
            $singleFullPrice = $room['price']->getEffectivePrice();
            $roomPrice = $singleFullPrice * $adultCount + $singleFullPrice * $room['persons']['children'];
            $singleDiscountPrice = $room['price']->getEffectiveDiscountPrice();
            $discount = ($singleDiscountPrice === null) ? 0
                : ($adultCount * ($singleDiscountPrice - $singleFullPrice));
            $ret += $roomPrice + $discount;

            if (isset($outPriceInfo))
            {
                $outPriceInfo['rooms'][] = [
                    'name' => $room['priceType']->getName(),
                    'adults' => $room['persons']['adults'],
                    'children' => $room['persons']['children'],
                    'price' => $singleDiscountPrice ?? $singleFullPrice,
                    'price_children' =>  $room['price']->getEffectiveChildPrice(),
                    'price_total' => $roomPrice + $discount,
                ];
            }

            if (isset($outHtmlSummary))
            {
                $label =  '1x '. $room['priceType']->getName() .' [Personen: '. $adultCount .' x <strong>'.
                    Util::formatPrice($singleFullPrice) .'</strong>';
                if ($room['persons']['children'] != 0)
                {
                    $label .= ', Kinder: '. $room['persons']['children'] .' x <strong>'.
                        Util::formatPrice($room['price']->getEffectiveChildPrice()) .'</strong>';
                }
                $label .= ']';
                $outHtmlSummary[] = [
                    'value' => $roomPrice,
                    'label' => $label
                ];
                if ($singleDiscountPrice !== null)
                {
                    $outHtmlSummary[] = [
                        'value' => $discount,
                        'label' => $adultCount .'x '.
                            Util::formatPrice($singleFullPrice - $singleDiscountPrice) .' Rabatt'
                    ];
                }
                if ($bookingRequest->getInsurance() && $adultCount > 0)
                {
                    $curAssessmentBasis = $insuranceAssessmentBasis + ($singleDiscountPrice ?? $singleFullPrice);
                    $insurancePrice = $this->getDoctrine()->getRepository('AppBundle:TravelInsurancePrice')
                        ->findOneByInsuranceIdAndAssessmentBasis($bookingRequest->getInsurance()->getId(),
                            $curAssessmentBasis);
                    $insurancePriceValue = $insurancePrice->getPrice() > 0 ? $insurancePrice->getPrice()
                        : round($insurancePrice->getPercent() * $curAssessmentBasis / 100, 2);
                    $a = $adultCount * $insurancePriceValue;
                    $insuranceTotal += $a;
                    $ret += $a;
                    if (isset($insuranceHtmlSummary))
                    {
                        $insuranceHtmlSummary[] = [
                            'value' => $a,
                            'label' => $adultCount .'x RV '. $bookingRequest->getInsurance()->getName() .' ('.
                                $insurancePrice->getCode() .') <strong>'. Util::formatPrice($insurancePriceValue) .
                                '</strong>'
                        ];
                    }
                    if (isset($outPriceInfo))
                    {
                        $outPriceInfo['insurances'][] = [
                            'insurance' => $bookingRequest->getInsurance(),
                            'insurancePriceValue' => $insurancePriceValue,
                            'insurancePrice' => $insurancePrice,
                            'count' => $adultCount,
                        ];
                    }
                }
            }
        }

        if (isset($insuranceHtmlSummary))
        {
            $outHtmlSummary = array_merge($outHtmlSummary, $insuranceHtmlSummary);
        }
        if (isset($outPriceInfo))
        {
            $outPriceInfo['total'] = $ret;
            $outPriceInfo['totalWithoutInsurance'] = $ret - $insuranceTotal;
        }

        return $ret;
    }

    /**
     * @param TravelPeriodPrice[] $prices
     * @param $persons
     *
     * @return array
     */
    private function searchRooms($prices, $persons)
    {
        $ret = [];
        foreach ($prices as $price)
        {
            $priceType = $this->priceTypeById[$price->getPriceTypeId()];
            if ($priceType->getMax()         == $persons['total'] &&
                $priceType->getMaxAdults()   >= $persons['adults'] &&
                $priceType->getMinAdults()   <= $persons['adults'] &&
                $priceType->getMaxChildren() >= $persons['children'])
            {
                $ret[] = [
                    'priceType' => $priceType,
                    'persons' => $persons,
                    'price' => $price
                ];
            }
        }
        return $ret;
    }

    private function splitIntoTwoGroups($prices, $persons, $mode)
    {
        $group1 = [];
        $group2 = [];
        if($mode == 'equal')
        {
            $group1['adults'] = $group2['adults'] = $persons['adults'] / 2;
            $group1['children'] = $group2['children'] = $persons['children'] / 2;
            $group1['total'] = $group2['total'] = $group1['adults'] + $group2['children'];
        }
        elseif($mode = 'move_without_children')
        {
            $group1['adults'] = $persons['adults'] - 1;
            $group1['children'] = 0;
            $group1['total'] = $group1['adults'] + $group1['children'];

            $group2['adults'] = 1;
            $group2['children'] = 0;
            $group2['total'] = $group2['adults'] + $group2['children'];
        }
        $possibleRoomsGroup1 = $this->searchRooms($prices, $group1);
        $possibleRoomsGroup2 = $this->searchRooms($prices, $group2);

        return array_merge($possibleRoomsGroup1, $possibleRoomsGroup2);
    }


}