<?php

namespace App\Services;

use App\Crm\Config\DealerWebConfig;
use App\Facades\Settings;
use App\Facades\Site;
use DOMDocument;
use Illuminate\Http\Client\Response;
use Illuminate\Support\Facades\Http;
use Mtc\Crm\Contracts\EnquiryActionModel;
use Mtc\Crm\Contracts\EnquiryModel;
use Mtc\MercuryDataModels\ApiNotification;
use App\Services\DealershipDetection;
use Mtc\MercuryDataModels\Booking;
use Mtc\MercuryDataModels\Dealership;
use Mtc\MercuryDataModels\Vehicle;
use Mtc\VehicleReservations\Reservation;

/**
 * Class DealerWebLmsApi
 *
 * Handles communication with the DealerWeb LMS API to send lead data.
 */
class DealerWebLmsApi
{
    /**
     * API endpoint for the DealerWeb LMS.
     *
     * @var string
     */
    private string $endpoint;

    /**
     * Stores the response message.
     *
     * @var string|null
     */
    protected ?string $responseMessage = null;

    /**
     * @var Response
     */
    private \Illuminate\Http\Client\Response $response;

    /**
     * DealerWebLmsApi constructor.
     *
     * @param protected readonly DealerWebConfig $config The configuration instance.
     */
    public function __construct(
        protected readonly DealerWebConfig $config,
        private readonly DealershipDetection $dealershipDetection
    ) {
        $endpoint = Settings::get('dealerweblms-endpoint');
        if (!empty($endpoint)) {
            $this->endpoint = $endpoint;
        }
    }

    /**
     * Gets the response message from the last API call.
     *
     * @return string|null The response message or null if not set.
     */
    public function getResponseMessage(): ?string
    {
        return $this->responseMessage;
    }

    /**
     * Sends lead data to the DealerWeb LMS.
     *
     * @param EnquiryModel $enquiry The enquiry model containing lead details.
     * @param EnquiryActionModel $action The action model for the enquiry.
     * @return bool True if the lead was sent successfully, false otherwise.
     */
    public function sendLead(EnquiryModel $enquiry, EnquiryActionModel $action): bool
    {
        return $this->submitLead($this->mapEnquiry($enquiry));
    }

    public function sendReservation(Reservation $reservation): bool
    {
        return $this->submitLead($this->mapReservation($reservation));
    }

    public function sendServiceBooking(Booking $booking): bool
    {
        return $this->submitLead($this->mapServiceBooking($booking));
    }

    protected function mapServiceBooking(Booking $booking): array
    {
        $dealerwebDealerId = Dealership::query()
            ->where('id', $booking->location_id)
            ->first()
            ?->data['dealerweb-dealer-id'] ?? '';

        $enquiry_details = [
            'Forename' => $booking->first_name,
            'Surname' => $booking->last_name,
            'Email' => $booking->email,
            'Tel_Mobile' => $booking->phone_number,
        ];

        $vehicle_data = [
            'URL' => null,
            'Model' => ($booking->vehicle_data['make'] ?? '') . ' ' . ($booking->vehicle_data['model'] ?? ''),
            'NewUsed' => 'U',
        ];

        $includeInDetailsContent[] = 'Registration: ' . $booking->registration_number ?? '';
        $includeInDetailsContent[] = 'Mileage: ' . $booking->mileage ?? '';
        $includeInDetailsContent[] = 'Booking Date: ' . $booking->booking_time ?? '';
        $includeInDetailsContent[] = 'Delivery Option: ' . $booking->deliveryOption?->name ?? '';
        $includeInDetailsContent[] = 'Make: ' . $booking->vehicle_data['make'] ?? '';
        $includeInDetailsContent[] = 'Model: ' . $booking->vehicle_data['model'] ?? '';
        $includeInDetailsContent[] = 'Transmission: ' . $booking->vehicle_data['transmission'] ?? '';
        $includeInDetailsContent[] = 'Fuel: ' . $booking->vehicle_data['fuel_type'] ?? '';
        $includeInDetailsContent[] = 'Year: ' . $booking->vehicle_data['manufacture_year'] ?? '';
        $includeInDetailsContent[] = 'Registered: ' . $booking->vehicle_data['registration_date'] ?? '';
        $includeInDetailsContent[] = 'Service Plan: ' . $booking->data['service_plan'] ?? '';

        // Aggregate include_in_details_content into a single "Details" field
        $detailsContent = implode(' ', $includeInDetailsContent);
        $enquiry_details['Detail'] = "<![CDATA[{$detailsContent}]]>";

        return array_merge([
            'Username' => $this->config->username(),
            'DealerNo' => $dealerwebDealerId,
            'ThirdParty' => $this->config->dealerNo(),
            'Password' => $this->config->password(),
            'ThirdPartyID' => $booking->id,
            'Version' => '7',
            'LeadType' => 'USE',
            'EnquiryType' => null,
        ], $enquiry_details, $vehicle_data);
    }

    /**
     * Maps enquiry data to the format required by the DealerWeb LMS.
     *
     * @param EnquiryModel $enquiry The enquiry model to map.
     * @return array The mapped enquiry data.
     */
    protected function mapEnquiry(EnquiryModel $enquiry): array
    {
        $vehicle_data = [];
        $lead_type = 'NWC';
        $autonomy_dealer_id = null;

        if ($enquiry->reason_type === 'vehicle') {
            $lead_type = 'NWC';

            $vehicleId = $enquiry->reason->id ?? null;
            $vehicle_data = $this->mapVehicle($vehicleId);

            // Set the lead type to used car if vehicle is set that way
            if ($vehicle_data['NewUsed'] === 'U') {
                $lead_type = 'USE';
            }
        }

        if ($enquiry->reason_type === 'valuation') {
            $lead_type = 'USE';

            // Get the vehicle of interest from hidden form field "Vehicle"
            $vehicleHiddenField = $enquiry->getNormalizedAnswers()
                ->filter(fn($detail) => $detail['question'] === 'Vehicle' && !empty($detail['answer']))
                ->first();

            // Add vehicle data for the vehicle of interest
            if (!empty($vehicleHiddenField)) {
                $vehicle_of_interest_id = $vehicleHiddenField['answer'];

                $vehicle_data = array_merge($vehicle_data, $this->mapVehicle($vehicle_of_interest_id));
            }

            // Add part exchange data to Dealerweb fields from the vehicle being valued
            $part_exchange_data = [
                'PexMake' => $enquiry->reason->make ?? null,
                'PexModel' => $enquiry->reason->model ?? null,
                'PexRegNo' => $enquiry->reason->registration ?? null,
                'PexMileage' => $enquiry->reason->mileage ?? null,
            ];

            $vehicleDetail = $enquiry->getNormalizedAnswers()
                ->filter(fn($detail) => $detail['question'] === 'Vehicle' && !empty($detail['answer']))
                ->toArray();

            // Set the vehicle of interest as the dealership location
            if (!empty($vehicleDetail)) {
                $vehicleDetail = reset($vehicleDetail);
                $vehicle_of_interest_id = $vehicleDetail['answer'];
                $vehicle = Vehicle::find($vehicle_of_interest_id);
                $autonomy_dealer_id = $vehicle->dealership->id ?? $autonomy_dealer_id;
            }

            $vehicle_data = array_merge($vehicle_data, $part_exchange_data);
        }

        if ($enquiry->reason_type === 'car-configuration') {
            $lead_type = 'NWC';

            $vehicle_data = [
                'URL' => $enquiry->source,
                'Model' => $enquiry->reason?->model?->make?->name . ' ' . $enquiry->reason?->model?->model?->name,
                'NewUsed' => 'N',
            ];
        }

        if ($enquiry->reason_type === 'new-car') {
            $vehicle_data = [
                'URL' => Site::newCarUrl($enquiry->reason),
                'Model' => $enquiry->reason->make?->name
                    . ' ' . $enquiry->reason->model?->name
                    . ' ' . $enquiry->reason->name,
                'NewUsed' => 'N',
            ];
        }

        foreach ($enquiry->objects as $object) {
            if ($object->object_type === 'vehicle' && $object->object && empty($vehicle_data)) {
                $vehicle_data = $this->mapVehicle($object->object->id);
                $autonomy_dealer_id = $object->object->dealership->id ?? $autonomy_dealer_id;

                if (($vehicle_data['NewUsed'] ?? '') === 'U') {
                    $lead_type = 'USE';
                }
            }

            if ($object->object_type === 'valuation' && $object->object) {
                $lead_type = 'USE';
                $part_exchange_data = [
                    'PexMake' => $object->object->make ?? null,
                    'PexModel' => $object->object->model ?? null,
                    'PexRegNo' => $object->object->registration ?? null,
                    'PexMileage' => $object->object->mileage ?? null,
                ];
                $vehicle_data = array_merge($vehicle_data, $part_exchange_data);
            }
        }

        $enquiry_details = $this->mapDetails($enquiry, $vehicle_data);

        $autonomy_dealer_id = $enquiry_details['dealer_id'] ?? $autonomy_dealer_id;

        $dealerwebDealerId = $this->getDealer($enquiry, $autonomy_dealer_id)->data['dealerweb-dealer-id']
            ?? null;

        // Send the translated Dealer Web ID through email instead of our Dealership ID
        $enquiry_details['dealer_id'] = $dealerwebDealerId;

        return array_merge([
            'Username' => $this->config->username(),
            'DealerNo' => $dealerwebDealerId,
            'ThirdParty' => $this->config->dealerNo(),
            'Password' => $this->config->password(),
            'ThirdPartyID' => $enquiry->id,
            'Version' => '7',
            'LeadType' => $lead_type,
            'EnquiryType' => $enquiry->reason_type === 'valuation' ? 'VMC' : null,
        ], $enquiry_details, $vehicle_data);
    }

    protected function mapReservation(Reservation $reservation): array
    {
        $vehicleId = $reservation->vehicle_id;
        $vehicle_data = $this->mapVehicle($vehicleId);
        $enquiry_details = $this->mapReservationDetails($reservation, $vehicle_data);

        return array_merge([
            'Username' => $this->config->username(),
            'DealerNo' => $reservation->vehicle?->dealership?->data['dealerweb-dealer-id'] ?? null,
            'ThirdParty' => $this->config->dealerNo(),
            'Password' => $this->config->password(),
            'ThirdPartyID' => 'R' . $reservation->id,
            'Version' => '7',
            'LeadType' => $vehicle_data['NewUsed'] === 'U' ? 'USE' : 'NWC',
            'EnquiryType' => null,
        ], $enquiry_details, $vehicle_data);
    }

    protected function getDealer(EnquiryModel $enquiry, $dealer_id = null): ?Dealership
    {
        $dealer_id = $enquiry->reason->dealership->id ?? $dealer_id;

        $postcode = $enquiry->data['postcode'] ?? null;

        $fallbackDealerId = $this->config->fallbackDealership();

        return $this->dealershipDetection->determineDealer(
            $fallbackDealerId,
            $dealer_id,
            $postcode,
        );
    }

    /**
     * Maps enquiry details to the format required by the DealerWeb LMS.
     *
     * @param EnquiryModel $enquiryModel
     * @return array The mapped details array.
     */
    protected function mapDetails(EnquiryModel $enquiryModel, $vehicle_data = []): array
    {
        $mappedDetails = [];
        $includeInDetailsContent = [];

        // Get normalized answers keyed by question_id for lookup
        $answers = $enquiryModel->getNormalizedAnswers()->keyBy('question_id');

        // Loop through each question to check for 'dealer-web-lms-field' mapping
        foreach ($enquiryModel->data['questions'] as $data_question_id => $data_question) {
            $fieldMapping = $data_question['dealer-web-lms-field'] ?? null;
            $field_value = $answers[$data_question_id]['answer'] ?? null;

            // Turn Booleans into readable strings
            if ($data_question['component'] === 'Boolean') {
                $field_value = ($field_value == '1' || $field_value === 1) ? 'Yes' : 'No';
            }

            if (is_array($field_value)) {
                if ($fieldMapping === 'marketing_preference') {
                    $field_value_uppercase = collect($field_value)->map(fn($value) => is_string($value)
                        ? strtoupper($value)
                        : $value)->toArray();

                    $mappedDetails['NoSMS'] = !in_array('SMS', $field_value_uppercase);
                    $mappedDetails['NoEmail'] = !in_array('EMAIL', $field_value_uppercase);
                    $mappedDetails['NoMail'] = !in_array('MAIL', $field_value_uppercase)
                        && !in_array('POST', $field_value_uppercase);
                }
            } elseif ($fieldMapping === 'include_in_details_content') {
                $label = $answers[$data_question_id]['question'] ?? null;
                $includeInDetailsContent[] = "{$label}: {$field_value}";
            } elseif (strtoupper((string) $fieldMapping) === 'DEALERNO') {
                $mappedDetails['dealer_id'] = $field_value;
            } elseif (!empty($fieldMapping) && !empty($field_value)) {
                $mappedDetails[$fieldMapping] = $field_value;
            }
        }

        if ($enquiryModel->reason_type === 'valuation') {
            $includeInDetailsContent[] = 'Valuation: ' . $this->getValuationAmount($enquiryModel);

            if (!empty($vehicle_data)) {
                $includeInDetailsContent[] = 'Vehicle of Interest Model: ' . ($vehicle_data['Model'] ?? '');
                $includeInDetailsContent[] = 'Vehicle of Interest URL: ' . ($vehicle_data['URL'] ?? '');
            }
        }

        if ($enquiryModel->reason_type === 'offer') {
            $includeInDetailsContent[] = 'Offer: ' . $enquiryModel->reason->name ?? '';
        }

        // Aggregate include_in_details_content into a single "Details" field
        if (!empty($includeInDetailsContent)) {
            $detailsContent = implode(' ', $includeInDetailsContent);
            $mappedDetails['Detail'] = "<![CDATA[{$detailsContent}]]>";
        }

        if ($enquiryModel->reason_type === 'car-configuration') {
            // get associative array of section selections, resulting in structure:
            // [
            //      'Editions' => ['Platinum'],
            //      'Extras' => ['SatNav', 'Reversing Camera'],
            // ]
            $config_selections = collect($enquiryModel->reason?->sections)->mapToGroups(fn ($section) => [
                $section->section?->name => $section->details['name'] ?? null
            ])->toArray();

            // convert section selections to a string per section
            $includeInDetailsContent = collect($config_selections)->map(fn ($selection, $section_name)
                => $section_name . ': ' . implode(', ', $selection))
                ->toArray();

            $detailsContent = implode('; ', $includeInDetailsContent);
            $mappedDetails['Detail'] = "<![CDATA[{$detailsContent}]]>";
        }

        return $mappedDetails;
    }

    protected function mapReservationDetails(Reservation $reservation, $vehicle_data = []): array
    {
        $mappedDetails = [
            'Title' => 'Mx',
            'Surname' => $reservation->name,
            'Email' => $reservation->email,
            'Tel_Mobile' => $reservation->contact_number,
        ];

        $detailsArray = [
            'Vehicle Registration: ' . $reservation->vehicle?->vrm_condensed ?? '',
            'Amount Paid: ' . $reservation->amount,
            'Reservation Status: ' . $reservation->status,
        ];

        $detailsContent = implode(' ', $detailsArray);
        $mappedDetails['Detail'] = "<![CDATA[{$detailsContent}]]>";

        return $mappedDetails;
    }

    /**
     * Maps vehicle data to the format required by the DealerWeb LMS.
     *
     * @param int|null $vehicle_id The ID of the vehicle to map.
     * @param mixed $enquiry
     * @param bool $part_exchange Include part exchange details
     * @return array The mapped vehicle data.
     */
    protected function mapVehicle($vehicle_id): array
    {
        $vehicle_data = [];
        $vehicle = Vehicle::find($vehicle_id);

        if (!empty($vehicle)) {
            $newUsed = $vehicle->is_new ? 'N' : 'U';
            $vehicle_full_model_name = '';

            if (!empty($vehicle->make->name) && !empty($vehicle->model->name)) {
                $vehicle_full_model_name = trim($vehicle->make->name
                    . ' '
                    . $vehicle->model->name
                    . ' '
                    . $vehicle->vrm_condensed);
            }

            $vehicle_data = [
                'URL' => Site::vehicleUrl($vehicle),
                'Model' => $vehicle_full_model_name,
                'NewUsed' => $newUsed,
            ];
        }

        return $vehicle_data;
    }

    /**
     * Submits the lead data to the DealerWeb LMS via SOAP request.
     *
     * @param array $lead_data The lead data array.
     * @return bool True if processed successfully
     */
    protected function submitLead(array $lead_data): bool
    {
        // Render the SOAP XML using Blade
        $soapEnvelope = view('dealer_web_lead', ['lead_data' => $lead_data])->render();

        // HTTP Headers
        $headers = [
            'Content-Type' => 'text/xml; charset=utf-8',
            'SOAPAction' => "http://services-dealerweb.8tech.co.uk/lms/AddLead",
        ];

        if (empty($this->endpoint)) {
            $this->responseMessage = 'The endpoint has not been set.';
            return false;
        }

        // Send the SOAP request
        $this->response = Http::withHeaders($headers)
            ->withBody($soapEnvelope, 'text/xml')
            ->post($this->endpoint);

        $api_notification = [
            'provider' => 'dealer-web-lms',
            'processed' => false,
            'headers' => $headers,
            'data' => [
                'body' => $soapEnvelope,
                'request' => $lead_data,
                'result' => $this->response->body(),
                'endpoint' => $this->endpoint,
            ],
            'data_model' => DealerWebLmsApi::class,
        ];

        // Check if the response was received successfully
        if (!$this->response->successful()) {
            ApiNotification::query()->create($api_notification);
            return false;
        }

        $doc = new DOMDocument('1.0', 'utf-8');
        $soapResponse = $this->response->body();
        $doc->loadXML($soapResponse);

        // Find the 'AddLeadResult' node within the response
        $addLeadResults = $doc->getElementsByTagName("AddLeadResult");
        if ($addLeadResults->length > 0) {
            $addLeadResult = $addLeadResults->item(0);

            // Retrieve values from the AddLeadResult element
            $customerId = $addLeadResult->getElementsByTagName("CustomerID")->item(0)->nodeValue ?? null;
            $code = $addLeadResult->getElementsByTagName("ErrorCode")->item(0)->nodeValue ?? null;
            $responseMessage = $addLeadResult->getElementsByTagName("ErrorMessage")->item(0)->nodeValue ?? null;

            // Set response message and check if processing succeeded
            $this->responseMessage = $responseMessage ?? 'Unknown response';
            $isProcessed = ((int)$code === 0);
        } else {
            $code = $customerId = null;
            $isProcessed = false;
            $this->responseMessage = 'AddLeadResult element not found in response';
        }

        // Determine if the lead was processed successfully
        $api_notification['processed'] = $isProcessed;
        $api_notification['data']['message'] = $this->responseMessage;
        $api_notification['data']['code'] = $code;
        $api_notification['data']['customerId'] = $customerId;

        // Log the response details
        ApiNotification::query()->create($api_notification);

        return $isProcessed;
    }

    protected function getValuationAmount(EnquiryModel $enquiryModel): string
    {
        $currency_symbol = match (strtoupper((string) Settings::get('app-details-currency'))) {
            'GBP' => '£',
            'EUR' => '€',
            default => '',
        };

        return empty($enquiryModel->reason->adjusted_average_price)
            ? $currency_symbol . $enquiryModel->reason->average_price
            : $currency_symbol . $enquiryModel->reason->adjusted_average_price;
    }
}
