<?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;
use App\Facades\Feature;
use App\Facades\Settings;
use Mtc\VehicleValuation\Models\VehicleValuation;

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
    {
        $enquiry = $this->processRequest($request, true);

        if ($enquiry) {
            return [
                'status' => 'ok',
                'message' => $this->getSuccessMessage($request),
                'redirect_url' => $request->form()->redirect_url,
                'enquiry_id' => $enquiry->id,
                'enquiry_vehicle_stats' => $this->getVehicleStatsSummary($enquiry),
            ];
        }
        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'),
                            'gclid' => $request->input('gclid'),
                        ],
                        'ip_address' => $request->ip(),
                    ],
                    'assigned_user_id' => $request->form()->initial_assignee,
                ]);
            $this->linkObjects($record, $request);
            $this->saveAnswers($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';
    }

    private function getVehicleStatsSummary(Enquiry $enquiry): array
    {
        if (
            !Feature::isEnabled('enquiry-vehicle-stats')
            || !Settings::get('crm-enquiry-vehicle-stats-enabled', false)
        ) {
            return [];
        }

        $vehicleObject = $enquiry->objects()->where('object_type', 'vehicle')->first();
        if (!$vehicleObject) {
            return [];
        }

        $vehicle = Vehicle::find($vehicleObject->object_id);
        if (!$vehicle) {
            return [];
        }

        $days = Settings::get('crm-enquiry-vehicle-stats-days', null);
        $views = $days ? $vehicle->getViewsForDays($days) : null;
        $threshold = (int) Settings::get('crm-enquiry-vehicle-stats-views-threshold', 0);

        return [
            'days' => $days,
            'views' => $views > $threshold ? $views : null,
            'total_enquiries' => $vehicle->allEnquiries()->count(),
        ];
    }

    /**
     * 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): string
    {
        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 ?? '',
            'valuation_id' => VehicleValuation::query()->find($id)->registration ?? '',
            'car_configuration_id' => CustomerCarConfiguration::query()->find($id)?->model?->name ?? '',
            'car_configuration_model_id' => CarConfiguratorModel::query()->find($id)->name ?? '',
            default => (string)$id
        };
    }

    /**
     *
     * @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;
    }

    /**
     * Save enquiry answers to the dedicated answers table
     */
    private function saveAnswers(Enquiry $enquiry, SubmitEnquiryRequest $request): void
    {
        // Delete existing answers if this is an update
        $enquiry->answers()->delete();

        collect($request->input('questions'))
            ->each(function ($answer, $questionId) use ($enquiry, $request) {
                $question = $request->form()->questions->where('id', $questionId)->first();

                // Only store answer if question exists
                if (!$question) {
                    return;
                }

                // Handle file uploads - get the path from details since it was already processed
                if ($question->type === 'file_upload') {
                    $detailEntry = collect($enquiry->details)
                        ->firstWhere('id', $questionId);
                    $answer = $detailEntry['answer'] ?? $answer;
                }

                // Get display value for related objects (e.g., dealership name instead of ID)
                $displayValue = null;
                if ($this->isRelatedObject($question->type) && !empty($answer)) {
                    $displayValue = $this->getRelatedObject($question->type, (int) $answer);
                } elseif ($question->type === 'select_field' && !empty($answer)) {
                    $displayValue = $this->getSelectFieldDisplayValue($question, $answer);
                }

                $enquiry->answers()->create([
                    'question_id' => (int) $questionId,
                    'question_name' => $question->name,
                    'answer_value' => is_array($answer) ? null : (string) $answer,
                    'answer_display_value' => $displayValue,
                    'answer_array' => is_array($answer) ? $answer : null,
                    'answer_type' => $this->determineAnswerType($question, $answer),
                    'crm_field_config' => $this->extractCrmFieldConfig($question),
                ]);
            });
    }

    /**
     * Extract CRM field configuration from question data
     */
    private function extractCrmFieldConfig($question): ?array
    {
        if (empty($question->data)) {
            return null;
        }

        $crmFields = collect($question->data)
            ->filter(fn ($value, $key) => Str::endsWith($key, '-field') || Str::contains($key, 'crm'))
            ->toArray();

        return !empty($crmFields) ? $crmFields : null;
    }

    /**
     * Resolve display value for select_field questions backed by a data source
     */
    private function getSelectFieldDisplayValue($question, $answer): ?string
    {
        $source = $question->data['meta']['options']['source'] ?? null;

        if (!$source || !is_numeric($answer)) {
            return null;
        }

        $id = (int) $answer;

        return match ($source) {
            'dealerships' => Dealership::query()->find($id)?->name,
            'pages', 'blog-pages', 'news-pages' => Page::query()->find($id)?->title,
            'offers' => VehicleOffer::query()->find($id)?->name,
            'vehicles' => Vehicle::query()->find($id)?->title,
            default => null,
        };
    }

    /**
     * Determine the answer type based on question type and answer value
     */
    private function determineAnswerType($question, $answer): string
    {
        if ($question?->type === 'file_upload') {
            return 'file';
        }

        if (is_array($answer)) {
            return 'array';
        }

        if ($this->isRelatedObject($question?->type)) {
            return $question->type;
        }

        return 'text';
    }
}
