<?php

namespace App;

use App\Http\Requests\PartialEnquirySubmissionRequest;
use App\Http\Requests\SubmitEnquiryRequest;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Request;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Mtc\Crm\Models\Enquiry;
use Mtc\Crm\Models\Scopes\OnlySubmitted;
use Mtc\MercuryDataModels\CarConfiguratorModel;
use Mtc\MercuryDataModels\CustomerCarConfiguration;
use Mtc\MercuryDataModels\Dealership;
use Mtc\MercuryDataModels\NewCar;
use Mtc\MercuryDataModels\Page;
use Mtc\MercuryDataModels\Property;
use Mtc\MercuryDataModels\Vehicle;
use Mtc\MercuryDataModels\VehicleOffer;

class EnquiryFormHandler
{
    /**
     * @param SubmitEnquiryRequest $request
     * @return Enquiry
     */
    public function partialSubmit(PartialEnquirySubmissionRequest $request): Model
    {
        return Enquiry::query()
            ->withoutGlobalScope(OnlySubmitted::class)
            ->updateOrCreate([
                'id' => $request->input('enquiry_id'),
            ], [
                'reason_id' => $this->getReasonId($request),
                'reason_type' => $this->getReasonType($request),
                'form_id' => $request->input('id'),
                'email' => $this->getEmailFromRequest($request),
                'type_id' => $request->form()->type_id,
                'source' => $request->input('source'),
                'status_id' => 0,
                'title' => $this->getTitleFromRequest($request),
                'message' => '',
                'details' => $this->details($request),
                'valuation_id' => $request->input('valuation_id'),
                'data' => [
                    'questions' => $request->form()?->questions()->pluck('data', 'id'),
                    'ip_address' => $request->ip(),
                ],
                'assigned_user_id' => $request->form()->initial_assignee,
            ]);
    }

    /**
     * Handle incoming Request
     *
     * @param SubmitEnquiryRequest $request
     * @return array|string[]
     */
    public function handle(SubmitEnquiryRequest $request): array
    {
        if ($this->processRequest($request)) {
            return [
                'status' => 'ok',
                'message' => $this->getSuccessMessage($request),

            ];
        }
        return [
            'status' => 'error',
            'message' => $this->getErrorMessage()
        ];
    }

    public function handleAndReturn(SubmitEnquiryRequest $request): Enquiry|false
    {
        return $this->processRequest($request, true);
    }

    /**
     * Process enquiry request to record
     * @param SubmitEnquiryRequest $request
     * @param bool $return
     * @return bool|Enquiry
     */
    protected function processRequest(SubmitEnquiryRequest $request, bool $return = false): bool|Enquiry
    {
        try {
            /** @var Enquiry $record */
            $record = Enquiry::query()
                ->withoutGlobalScope(OnlySubmitted::class)
                ->updateOrCreate([
                    'id' => $request->input('enquiry_id'),
                ], [
                    'reason_id' => $this->getReasonId($request),
                    'reason_type' => $this->getReasonType($request),
                    'form_id' => $request->input('id'),
                    'source' => Str::limit($request->input('source', ''), 490),
                    'email' => $this->getEmailFromRequest($request),
                    'type_id' => $request->form()->type_id,
                    'status_id' => $request->form()->initial_status_id,
                    'title' => $this->getTitleFromRequest($request),
                    'message' => '',
                    'details' => $this->details($request),
                    'valuation_id' => $request->input('valuation_id'),
                    'data' => [
                        'questions' => $request->form()?->questions()->pluck('data', 'id'),
                        'meta' => [
                            'source_url' => $request->header('x-path'),
                            'utm_campaign' => $request->input('utm_campaign'),
                            'utm_content' => $request->input('utm_content'),
                            'utm_medium' => $request->input('utm_medium'),
                            'utm_source' => $request->input('utm_source'),
                            'utm_term' => $request->input('utm_term'),
                        ],
                        'ip_address' => $request->ip(),
                    ],
                    'assigned_user_id' => $request->form()->initial_assignee,
                ]);
            $this->linkObjects($record, $request);
            if ($return) {
                return $record;
            }
        } catch (\Exception $exception) {
            Log::error('Failed to process Enquiry Submission', [
                'input' => $request->input(),
                'error' => $exception->getMessage(),
            ]);
            return false;
        }
        return true;
    }

    protected function details(SubmitEnquiryRequest $request)
    {
        return collect($request->input('questions'))
            ->map(function ($answer, $id) use ($request) {
                $question = $request->form()->questions->where('id', $id)->first();
                if ($question?->type == 'file_upload' && $request->has('files.' . $id)) {
                    if (is_array($request->file('files.' . $id))) {
                        $answer = collect($request->file('files.' . $id))
                            ->map(fn($file) => $this->uploadFile($file));
                    } else {
                        $answer = $this->uploadFile($request->file('files.' . $id));
                    }
                }
                return [
                    'id' => $id,
                    'answer' => $answer,
                    'question' => $question->name ?? '',
                ];
            })->toArray();
    }

    /**
     * Success message
     *
     * @param SubmitEnquiryRequest $request
     * @return string
     */
    protected function getSuccessMessage(SubmitEnquiryRequest $request): string
    {
        return $request->form()->success_message ?? '';
    }

    /**
     * Error message
     *
     * @return string
     */
    protected function getErrorMessage(): string
    {
        return 'Failed to submit your enquiry. Please make sure all required fields are filled and try again';
    }

    /**
     * Get the enquiry title/subject from form fields and request data
     *
     * @param SubmitEnquiryRequest $request
     * @return string
     */
    protected function getTitleFromRequest(SubmitEnquiryRequest $request): string
    {
        return $request->form()
            ->questions
            ->filter(fn($question) => $question->is_enquiry_subject)
            ->sortBy(fn($question) => $question->order)
            ->map(function ($question) use ($request) {
                $inputValue = $request->input('questions.' . $question->id);

                if ($this->isRelatedObject($question->type)) {
                    return $this->getRelatedObject($question->type, (int)$inputValue);
                }

                if (is_array($inputValue)) {
                    return implode(', ', $inputValue);
                }

                return $inputValue;
            })
            ->implode(' ');
    }

    /**
     * Find email field value based off the form fields and request
     *
     * @param SubmitEnquiryRequest $request
     * @return string|null
     */
    protected function getEmailFromRequest(SubmitEnquiryRequest $request): string|null
    {
        $question_id = $request->form()
            ->questions
            ->filter(fn($question) => $question->is_customers_email)
            ->first()
            ->id ?? null;

        return $question_id ? $request->input('questions.' . $question_id) : '';
    }

    /**
     * Check if the type is a related object
     *
     * @param string $type
     * @return bool
     */
    protected function isRelatedObject(string $type)
    {
        return in_array($type, [
            'page_id',
            'vehicle_id',
            'offer_id',
            'dealership_id',
            'valuation_id',
            'new_car_id',
            'property_id',
            'car_configuration_id',
            'car_configuration_model_id',
        ]);
    }

    protected function getRelatedObject(string $type, int $id)
    {
        return match ($type) {
            'page_id' => Page::query()->find($id)->title ?? '',
            'vehicle_id' => Vehicle::query()->find($id)->title ?? '',
            'offer_id' => VehicleOffer::query()->find($id)->name ?? '',
            'new_car_id' => NewCar::query()->find($id)->name ?? '',
            'dealership_id' => Dealership::query()->find($id)->name ?? '',
            'property_id' => Property::query()->find($id)->name ?? '',
            'car_configuration_id' => CustomerCarConfiguration::query()->find($id)?->model?->name ?? '',
            'car_configuration_model_id' => CarConfiguratorModel::query()->find($id)->name ?? '',
        };
    }

    /**
     *
     * @param Request $request
     * @return string|null
     */
    protected function getReasonType(Request $request): ?string
    {
        $reason = $request->form()
            ->questions
            ->filter(fn($question) => $this->isRelatedObject($question->type)
                && $request->filled('questions.' . $question->id))
            ->map(fn($question) => $this->mapToObjectType($question->type))
            ->first();
        return $reason;
    }

    /**
     * Get the reason ID for the enquiry
     *
     * @param Request $request
     * @return mixed
     */
    protected function getReasonId(Request $request): ?string
    {
        return $request->form()
            ->questions
            ->filter(fn($question) => $this->isRelatedObject($question->type)
                && $request->filled('questions.' . $question->id))
            ->map(fn($question) => $request->input('questions.' . $question->id))
            ->first();
    }

    protected function uploadFile(UploadedFile $file): string
    {
        try {
            $uploadPath = 'uploads/' . date('Y-M');
            $fileName = Carbon::now()->format('U') . pathinfo($file->getClientOriginalName(), PATHINFO_BASENAME);
            $extension = pathinfo($fileName, PATHINFO_EXTENSION);

            if (strlen($fileName) > 75) {
                $fileName = Str::random(75) . '.' . $extension;
            } else {
                $fileName = Str::slug(str_replace('.' . $extension, '', Str::limit($fileName, 75)))
                    . '.' . $extension;
            }

            $fileContents = $file->get();
            if ($fileContents === false) {
                Log::error('EnquiryFormHandler: Unable to load file');
            }

            $fullPath = $uploadPath . '/' . $fileName;

            // make sure CDN endpoint is not used as it will not work for uploads
            Config::set(
                'filesystems.disks.file-storage.endpoint',
                str_replace('cdn.', '', Config::get('filesystems.disks.file-storage.endpoint'))
            );
            Storage::disk('file-storage')->put(
                $fullPath,
                $fileContents,
            );

            return $fullPath;
        } catch (\Exception $exception) {
            Log::error('EnquiryFormHandler: File upload failed', [
                'exception' => $exception->getMessage(),
                'file' => $file,
            ]);
            return '';
        }
    }

    private function linkObjects(Enquiry $record, SubmitEnquiryRequest $request)
    {
        $request->form()
            ->questions
            ->filter(fn($question) => $this->isRelatedObject($question->type)
                && $request->filled('questions.' . $question->id))
            ->map(fn($question) => [
                'object_id' => $request->input('questions.' . $question->id),
                'object_type' => $this->mapToObjectType($question->type),
            ])
            ->each(fn($object) => $record->objects()->create($object));
    }

    private function mapToObjectType($type): ?string
    {
        $map = [
            'page_id' => 'page',
            'vehicle_id' => 'vehicle',
            'offer_id' => 'offer',
            'dealership_id' => 'dealership',
            'valuation_id' => 'valuation',
            'new_car_id' => 'new-car',
            'property_id' => 'property',
            'car_configuration_id' => 'car-configuration',
            'car_configuration_model_id' => 'car-config-model',
        ];

        return $map[$type] ?? $type;
    }
}
