<?php


namespace App\Core\Service;

use App\Core\Repository\PropertyCancellationPolicy\PropertyCancellationPolicyRepository;
use App\Core\Repository\PropertyRoom\PropertyRoomRepository;
use App\Core\Repository\PropertyChannelRoomRateCancellationPolicyMapping\PropertyChannelRoomRateCancellationPolicyMappingRepository;
use App\Core\Validator\PropertyCancellationPolicy\UpdateRoomRateChannelCancellationPolicyValidator;
use App\Core\Validator\PropertyCancellationPolicy\PropertyCancellationPolicyAddValidator;
use App\Core\Validator\PropertyCancellationPolicy\PropertyCancellationPolicyUpdateValidator;

use Carbon\Carbon;
use Illuminate\Http\Request;
use App;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Config;
use Exception;
use App\Exceptions\ApiErrorException;
use Illuminate\Support\Facades\DB;


class PropertyCancellationPolicyService
{
    private $propertyCancellationPolicyRepository;
    private $propertyRoomRepository;
    private $propertyChannelRoomRateCancellationPolicyMappingRepository;
    private $updateRoomRateChannelCancellationPolicyValidator;
    private $propertyCancellationPolicyAddValidator;
    private $propertyCancellationPolicyUpdateValidator ;

    public function __construct
    (
        Request $request,
        PropertyRoomRepository $propertyRoomRepository,
        PropertyChannelRoomRateCancellationPolicyMappingRepository $propertyChannelRoomRateCancellationPolicyMappingRepository,
        UpdateRoomRateChannelCancellationPolicyValidator $updateRoomRateChannelCancellationPolicyValidator,
        PropertyCancellationPolicyRepository $propertyCancellationPolicyRepository,
        PropertyCancellationPolicyUpdateValidator $propertyCancellationPolicyUpdateValidator,
        PropertyCancellationPolicyAddValidator $propertyCancellationPolicyAddValidator

    )
    {
        $this->request = $request;
        $this->propertyCancellationPolicyRepository = $propertyCancellationPolicyRepository;
        $this->propertyRoomRepository = $propertyRoomRepository;
        $this->propertyChannelRoomRateCancellationPolicyMappingRepository = $propertyChannelRoomRateCancellationPolicyMappingRepository ;
        $this->updateRoomRateChannelCancellationPolicyValidator = $updateRoomRateChannelCancellationPolicyValidator ;
        $this->propertyCancellationPolicyAddValidator = $propertyCancellationPolicyAddValidator ;
        $this->propertyCancellationPolicyUpdateValidator = $propertyCancellationPolicyUpdateValidator ;

    }



    public function select($param = [], $column = ['*'])
    {
        $response = ['status' => -1, 'message' => '', 'data' => null];
        try {
            $data = $this->propertyCancellationPolicyRepository->findByCriteria($param, $column);
            $response = [
                'status' => true,
                'data' => $data,
            ];

        } catch (ApiErrorException $e) {
            $response['message'] = $e->getMessage();
        } catch (Exception $e) {
            $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage();
            Log::error($message);
            $response['message'] = $e->getMessage();
        }

        return output($response);
    }

    public function create($param = [])
    {
        $response = ['status' => -1, 'message' => '', 'data' => null];
        try {

            $propertyData =
                [
                    "property_id" => fillOnUndefined($param, "property_id"),
                    "name" => fillOnUndefined($param, "name"),
                    'before_arrival' => fillOnUndefined($param, "before_arrival"),
                    'is_nonrefundable' => fillOnUndefined($param, "is_nonrefundable"),
                    'is_free_cancellation' => fillOnUndefined($param, "is_free_cancellation"),
                    'type' => fillOnUndefined($param, "type"),
                    'value' => fillOnUndefined($param, "value"),
                    'is_affected_price' => fillOnUndefined($param, "is_affected_price"),
                    'affect_price_action_type' => fillOnUndefined($param, "affect_price_action_type"),
                    'affect_price_type' => fillOnUndefined($param, "affect_price_type"),
                    'affect_price_value' => fillOnUndefined($param, "affect_price_value"),
                    'is_date_range' => fillOnUndefined($param, "is_date_range"),
                    'start_date' => fillOnUndefined($param, "start_date"),
                    'finish_date' => fillOnUndefined($param, "finish_date"),
                    "status" => fillOnUndefined($param, "status", 1),
                    "created_by" => fillOnUndefined($param, "user_id"),
                    "updated_by" => fillOnUndefined($param, "user_id"),
                    "created_at" => time(),
                    "updated_at" => time(),
                ];

            $propertyCreateResult = $this->propertyCancellationPolicyRepository->create($propertyData);

            if ($propertyCreateResult['status'] != 'success') {
                throw new Exception('api-unknown_error');
            }
            $data = Arr::except($propertyCreateResult["data"],['status','created_by','updated_by','created_at','updated_at']);
            $response = [
                'status' => true,
                'data' => $data
            ];
        } catch (ApiErrorException $e) {
            $response['message'] = implode(', ', $e->getMessageArr());
        } catch (Exception $e) {
            $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage();
            Log::error($message);
            $response['message'] = $e->getMessage();
        }

        return output($response);
    }

    public function update($id, $param = [])
    {

        $response = ['status' => -1, 'message' => '', 'data' => null];
        try {

            $updateResult = $this->propertyCancellationPolicyRepository->update($id, $param);
            if ($updateResult['status'] != 'success') {
                throw new Exception('api-unknown_error');
            }
            $updateData = $updateResult["data"];
            $data = Arr::except($updateData,['status','created_by','updated_by','created_at','updated_at']);
            $response = [
                'status' => true,
                'data' => $data
            ];

        } catch (ApiErrorException $e) {
            $response['message'] = implode(', ', $e->getMessageArr());
        } catch (Exception $e) {
            $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage();
            Log::error($message);
            $response['message'] = $e->getMessage();
        }

        return output($response);
    }

    public function getPropertyCancellationPolicyList($params){

        $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500];
        try {

            $propertyId = fillOnUndefined($params, 'property_id');
            $propertyCancellationPolicyRequest = [
                'criteria' => [
                    ['field' => 'status', 'condition' => '=', 'value' => 1],
                    ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId],

                ],
            ];
            $propertyCancellationPolicy = $this->propertyCancellationPolicyRepository->findByCriteria($propertyCancellationPolicyRequest, ['id', 'property_id', 'name', 'before_arrival', 'is_nonrefundable', 'is_free_cancellation', 'type','value', 'is_affected_price', 'affect_price_action_type', 'affect_price_type', 'affect_price_value', 'is_date_range', 'start_date', 'finish_date', 'status']);

            $propertyCancellationPolicy = $propertyCancellationPolicy ? $propertyCancellationPolicy : [] ;

            $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' =>  $propertyCancellationPolicy];

        } catch (ApiErrorException $e) {
            $response['message'] = implode(', ', $e->getMessageArr());
            $response['statusCode'] = 400;
        } catch (Exception $e) {
            $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage();
            Log::error($message);
            $response['message'] = $e->getMessage();
            $response['statusCode'] = 500;
        }

        return output($response);
    }

    public function createPropertyCancellationPolicy($params)
    {

        $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500];
        try {
            DB::beginTransaction();

            $validationResult = $this->propertyCancellationPolicyAddValidator->validate($params);
            if ($validationResult->errors()->first()) {
                $errors = $validationResult->errors()->all();
                throw new ApiErrorException($errors);
            }

            if($params['is_nonrefundable']){
                $type = 'PER';
                $value = 100 ;
            }elseif ($params['is_free_cancellation']){
                $type = 'PER';
                $value = 0 ;
            }else{
                $type = $params['type'] ;
                $value = $params['value'] ;
            }
            $createData =
                [
                    'property_id' => fillOnUndefined($params, 'property_id'),
                    'name' => isset($params['name']) && $params['name'] ? $params['name'] : null,
                    'before_arrival' => $params['before_arrival'],
                    'is_nonrefundable' => $params['is_nonrefundable'],
                    'is_free_cancellation' => $params['is_free_cancellation'],
                    'type' => $type,
                    'value' => $value,
                    'is_affected_price' => $params['affects_price'],
                    'affect_price_action_type' => $params['affects_price_action_type'],
                    'affect_price_type' => $params['affects_price_type'],
                    'affect_price_value' => $params['affects_price_value'],
                    'is_date_range' => $params['is_date_range'],
                    'start_date' => fillOnUndefined($params, 'start_date'),
                    'finish_date' => fillOnUndefined($params, 'finish_date'),
                    'user_id' => fillOnUndefined($params, 'user_id'),
                    'status' => 1,
                ];


            $createResult = $this->create($createData);

            if($createResult['status'] != 'success'){
                throw new ApiErrorException($createResult['message']);
            }

            $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $createResult['data']];

            DB::commit();
        } catch (ApiErrorException $e) {
            db::rollBack();
            $response['message'] = $e->getMessage();
        } catch (Exception $e) {
            DB::rollBack();
            $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage();
            Log::error($message);
            $response['message'] = $e->getMessage();
        }

        return output($response);
    }

    public function updatePropertyCancellationPolicy($params){

        $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500];
        try {

            $validationResult = $this->propertyCancellationPolicyUpdateValidator->validate($params);
            if ($validationResult->errors()->first()) {
                $errors = $validationResult->errors()->all();
                throw new ApiErrorException($errors);
            }

            if($params['is_nonrefundable']){
                $type = 'PER';
                $value = 100 ;
            }elseif ($params['is_free_cancellation']){
                $type = 'PER';
                $value = 0 ;
            }else{
                $type = $params['type'] ;
                $value = $params['value'] ;
            }

            $cancellationPolicyUpdateData =
                [
                    'name' => isset($params['name']) && $params['name'] ? $params['name'] : null,
                    'before_arrival' => $params['before_arrival'],
                    'is_nonrefundable' => $params['is_nonrefundable'],
                    'is_free_cancellation' => $params['is_free_cancellation'],
                    'type' => $type,
                    'value' => $value,
                    'is_affected_price' => $params['affects_price'],
                    'affect_price_action_type' => $params['affects_price_action_type'],
                    'affect_price_type' => $params['affects_price_type'],
                    'affect_price_value' => $params['affects_price_value'],
                    'is_date_range' => $params['is_date_range'],
                    'start_date' => fillOnUndefined($params, 'start_date'),
                    'finish_date' => fillOnUndefined($params, 'finish_date'),
                    'updated_by' => fillOnUndefined($params, 'user_id'),
                    'updated_at' => time()
                ];

            $cancellationPolicyId = fillOnUndefined($params, 'cancellation_policy_id');

            $cancellationPolicyResult = $this->update($cancellationPolicyId, $cancellationPolicyUpdateData);

            if ($cancellationPolicyResult['status'] != 'success') {
                throw new ApiErrorException(lang('Property Cancellation Policy data is not updated.'));
            }

            $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $cancellationPolicyResult['data']];

        } catch(ApiErrorException $e) {
            $response['message'] = implode(', ', $e->getMessageArr());
            $response['statusCode'] = 400;
        } catch (Exception $e) {
            $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage();
            Log::error($message);
            $response['message'] = $e->getMessage();
            $response['statusCode'] = 500;
        }

        return output($response);

    }

    public function getRoomRateChannelCancellationPolicy($params = [])
    {
        $response = ['status' => -1, 'message' => '', 'data' => null];
        try {

            $criteria = [
                'criteria' => [
                    ['field' => 'status', 'condition' => '=', 'value' => 1],
                    ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']],
                ],
                'with' => ['propertyRoomType', 'propertyRoomRateMapping.propertyRoomRateChannel.propertyRoomRateChannelCancellationPolicy', 'propertyRoomRateMapping.propertyRoomRate'],
                'orderBy' => [
                    ['field' => 'id', 'value' => 'ASC']
                ]
            ];

            $roomData = $this->propertyRoomRepository->findByCriteria($criteria, ['id', 'name', 'room_type_id', 'max_occupancy', 'max_adult', 'max_child',  'exclude_occupancy', 'room_size', 'room_size_type', 'room_type_count', 'room_count', 'bathroom_count',  'toilet_count', 'lounge_count', 'max_child_number']);

            $propertyCancellationPolicyRequest = [
                'criteria' => [
                    ['field' => 'status', 'condition' => '=', 'value' => 1],
                    ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']],
                ]
            ];
            $propertyCancellationPolicy = $this->propertyCancellationPolicyRepository->findByCriteria($propertyCancellationPolicyRequest, ['id', 'property_id',  'name', 'before_arrival', 'is_nonrefundable', 'is_free_cancellation', 'type','value', 'is_affected_price', 'affect_price_action_type', 'affect_price_type', 'affect_price_value', 'is_date_range', 'start_date', 'finish_date', 'status']);

            $return = [];

            foreach ($roomData as $room){

                if($room['property_room_rate_mapping']){
                    $item = $room;
                    $item['exclude_occupancy'] = json_decode($room['exclude_occupancy']);
                    $roomRateMappings = $room['property_room_rate_mapping'] ;
                    $mapping = [];
                    $addedMapping = 0 ;
                    foreach ($roomRateMappings as $roomRateMapping){
                        $propertyRoomRateChannel = null;
                        if(isset($roomRateMapping['property_room_rate_channel'])){
                            $propertyRoomRateChannel = collect($roomRateMapping['property_room_rate_channel'])
                                ->where('channel_id', '=', $params['channel_id'])
                                ->where('room_rate_mapping_id', '=', $roomRateMapping['id'])
                                ->first();
                        }

                        $mappingStatus = false ;
                        $propertyCancellationPolicyArray = [] ;
                        if($propertyRoomRateChannel){
                            if ($propertyRoomRateChannel['status'] == 1) {
                                $mappingStatus = true;
                                foreach ($propertyCancellationPolicy as $cancellationPolicy) {

                                    $propertyRoomRateChannelCancellationPolicy = [] ;
                                    if (isset($propertyRoomRateChannel['property_room_rate_channel_cancellation_policy'])) {
                                        $propertyRoomRateChannelCancellationPolicy = collect($propertyRoomRateChannel['property_room_rate_channel_cancellation_policy'])
                                            ->where('cancellation_policy_id', '=', $cancellationPolicy['id'])
                                            ->where('room_rate_channel_mapping_id', '=', $propertyRoomRateChannel['id'])
                                            ->values()->toArray();
                                    }

                                    $propertyCancellationPolicyArray[] = [
                                        'id' => $cancellationPolicy['id'],
                                        'property_id' => $cancellationPolicy['property_id'],
                                        'name' => $cancellationPolicy['name'],
                                        'before_arrival' => $cancellationPolicy['before_arrival'],
                                        'is_nonrefundable' => $cancellationPolicy['is_nonrefundable'],
                                        'is_free_cancellation' => $cancellationPolicy['is_free_cancellation'],
                                        'type' => $cancellationPolicy['type'],
                                        'value' => $cancellationPolicy['value'],
                                        'is_affected_price' => $cancellationPolicy['is_affected_price'],
                                        'affect_price_action_type' => $cancellationPolicy['affect_price_action_type'],
                                        'affect_price_type' => $cancellationPolicy['affect_price_type'],
                                        'affect_price_value' => $cancellationPolicy['affect_price_value'],
                                        'is_date_range' => $cancellationPolicy['is_date_range'],
                                        'start_date' => $cancellationPolicy['start_date'],
                                        'finish_date' => $cancellationPolicy['finish_date'],
                                        'is_selected' => $propertyRoomRateChannelCancellationPolicy ? true : false ,
                                    ];
                                }

                            }
                        }
                        $roomRateMapping['name'] = $roomRateMapping['property_room_rate']['name'];
                        $roomRateMapping['is_selected'] = $mappingStatus;
                        $roomRateMapping['has_date'] = $propertyRoomRateChannel['has_date'] == 0 ? false : true;
                        $roomRateMapping['end_date'] = $propertyRoomRateChannel['end_date'];
                        $roomRateMapping['start_date'] = $propertyRoomRateChannel['start_date'];
                        $roomRateMapping['room_rate_channel_mapping_id'] = $propertyRoomRateChannel['id'];
                        $roomRateMapping['room_rate_channel_cancellation_policy'] = $propertyCancellationPolicyArray;

                        unset($roomRateMapping['property_room_rate']);
                        unset($roomRateMapping['property_room_rate_channel']);
                        if($mappingStatus){
                            $addedMapping++;
                            $mapping[] = $roomRateMapping;
                        }
                    }
                    $item['property_room_rate_mapping'] = $mapping;
                    if($addedMapping > 0){
                        $return[] = $item ;
                    }
                }

            }

            $collection = collect($return);
            $sorted = $collection->sortBy('id')->values()->all();

            $response = [
                'status' => true,
                'data' => $sorted,
            ];

        } catch (ApiErrorException $e) {
            $response['message'] = $e->getMessage();
        } catch (Exception $e) {
            $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage();
            Log::error($message);
            $response['message'] = $e->getMessage();
        }

        return output($response);
    }

    public function updateRoomRateChannelCancellationPolicy($params = [])
    {
        $response = ['status' => -1, 'message' => '', 'data' => null];
        try {

            DB::beginTransaction();

            $validationResult = $this->updateRoomRateChannelCancellationPolicyValidator->validate($params);
            if ($validationResult->errors()->first()) {
                $errors = $validationResult->errors()->all();
                throw new ApiErrorException($errors);
            }

            $criteria = [
                'criteria' => [
                    ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']],
                ],
                'with' => ['propertyCancellationPolicy']
            ];

            $propertyChannelRoomRateCancellationPolicyMapping = $this->propertyChannelRoomRateCancellationPolicyMappingRepository->findByCriteria($criteria);
            $oldMappingData = collect($propertyChannelRoomRateCancellationPolicyMapping);

            $criteria = [
                'criteria' => [
                    ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']],
                    ['field' => 'id', 'condition' => '=', 'value' => $params['cancellation_policy_id']],
                ],
                'firstRow' => 1
            ];

            $mappedCancellationPolicy = $this->propertyCancellationPolicyRepository->findByCriteria($criteria);
            if(!$mappedCancellationPolicy) {
                throw new ApiErrorException('This Cancellation Policy not found');
            }
                if ($params['is_selected'] == true) {

                $oldDataMappingCollect = collect($oldMappingData)->map(function ($value){
                    return [
                        'id' => $value['id'] ,
                        'property_id' => $value['property_id'],
                        'cancellation_policy_id' => $value['cancellation_policy_id'],
                        'room_rate_channel_mapping_id' => $value['room_rate_channel_mapping_id'],
                        'name' => $value['property_cancellation_policy']['name'] ,
                        'before_arrival' => $value['property_cancellation_policy']['before_arrival'],
                        'is_nonrefundable' => $value['property_cancellation_policy']['is_nonrefundable'],
                        'is_free_cancellation' => $value['property_cancellation_policy']['is_free_cancellation'],
                        'type' => $value['property_cancellation_policy']['value'],
                        'value' => $value['property_cancellation_policy']['type'],
                        'is_affected_price' => $value['property_cancellation_policy']['is_affected_price'],
                        'affect_price_action_type' => $value['property_cancellation_policy']['affect_price_action_type'],
                        'affect_price_type' => $value['property_cancellation_policy']['affect_price_type'],
                        'affect_price_value' => $value['property_cancellation_policy']['affect_price_value'],
                    ] ;
                });

                $checkHasMapping =  $oldDataMappingCollect->where('cancellation_policy_id', '=', $params['cancellation_policy_id'])
                    ->where('room_rate_channel_mapping_id' ,'=', $params['room_rate_channel_mapping_id'])
                    ->where('property_id', '=', $params['property_id'])
                    ->first() ;

                $checkHasDay =  $oldDataMappingCollect
                        ->where('before_arrival', '=', $mappedCancellationPolicy['before_arrival'])
                        ->where('start_date', '=', $mappedCancellationPolicy['start_date'])
                        ->where('finish_date', '=', $mappedCancellationPolicy['finish_date'])
                        ->where('room_rate_channel_mapping_id', '=', $params['room_rate_channel_mapping_id'])
                        ->where('property_id', '=', $params['property_id'])
                        ->first() ;

                if($checkHasMapping || $checkHasDay) {
                    throw new ApiErrorException('This mapping added before');
                }else{

                    $insertData = [] ;
                    $insertData[] = [
                        'property_id' => $params['property_id'],
                        'cancellation_policy_id' => $params['cancellation_policy_id'],
                        'room_rate_channel_mapping_id' => $params['room_rate_channel_mapping_id'],
                        'status' => 1,
                        'created_by' => fillOnUndefined($params, 'user_id'),
                        'updated_by' => fillOnUndefined($params, 'user_id'),
                        'created_at' => Carbon::now()->timestamp,
                        'updated_at' => Carbon::now()->timestamp,
                    ];
                    if($insertData){
                        $createResult = $this->propertyChannelRoomRateCancellationPolicyMappingRepository->insert($insertData);
                        if ($createResult['status'] != 'success') {
                            throw new Exception('api-unknown_error');
                        }
                    }
                }
            }


            if($params['is_selected'] == false){
                $deleteThis = collect($propertyChannelRoomRateCancellationPolicyMapping)
                    ->where('cancellation_policy_id', '=', $params['cancellation_policy_id'])
                    ->where('room_rate_channel_mapping_id', '=', $params['room_rate_channel_mapping_id'])
                    ->where('property_id', '=', $params['property_id'])
                    ->keyBy('id')->keys()->toArray();

                if($deleteThis){
                    $deleteThisArray = $this->propertyChannelRoomRateCancellationPolicyMappingRepository->destroy($deleteThis);
                    if ($deleteThisArray['status'] != 'success') {
                        throw new Exception('api-unknown_error');
                    }
                }

            }


            $response = [
                'status' => true,
                'data' => null,
            ];
            DB::commit();

        } catch (ApiErrorException $e) {
            db::rollBack();
            $response['message'] = $e->getMessage();
        } catch (Exception $e) {
            DB::rollBack();
            $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage();
            Log::error($message);
            $response['message'] = $e->getMessage();
        }

        return output($response);
    }

}
