<?php

declare(strict_types=1);

namespace App\Services;

use App\Crm\Config\MotordatCrmConfig;
use App\Crm\EnquiryDataToProviderData;
use App\Crm\HandlesCrmApiResponse;
use App\Facades\Settings;
use Illuminate\Support\Facades\Http;
use Mtc\Crm\Contracts\EnquiryActionModel;
use Mtc\Crm\Contracts\EnquiryModel;
use Mtc\MercuryDataModels\ApiNotification;
use Mtc\MercuryDataModels\Booking;
use Mtc\MercuryDataModels\NewCar;
use Mtc\MercuryDataModels\Vehicle;
use Mtc\MercuryDataModels\VehicleOffer;
use Mtc\MercuryDataModels\VehicleValuation;
use Mtc\VehicleReservations\Reservation;

class MotordatCrmApi
{
    use EnquiryDataToProviderData;
    use HandlesCrmApiResponse;

    public function __construct(private readonly MotordatCrmConfig $config)
    {
    }

    private function endpoint(string $path = ''): string
    {
        $baseUrl = app()->isProduction()
            ? 'https://api.motordat.com'
            : 'https://oemapi.motordat.com';

        return rtrim($baseUrl, '/') . '/' . ltrim($path, '/');
    }

    public function sendLead(EnquiryModel $enquiry, EnquiryActionModel $action): bool
    {
        return $this->submitLead(
            $this->mapEnquiry($enquiry, $action),
            $enquiry->getMorphClass(),
            $enquiry->id
        );
    }

    public function sendReservation(Reservation $reservation): bool
    {
        return $this->submitLead(
            $this->mapReservationData($reservation),
            $reservation->getMorphClass(),
            $reservation->id
        );
    }

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

    protected function submitLead(array $data, string $data_model, int $reference): bool
    {
        $this->response = Http::timeout(60)
            ->withHeaders([
                'Content-Type' => 'application/json',
                'Vendor-Name' => $this->config->vendorName(),
            ])
            ->withBasicAuth(
                $this->config->username() ?? '',
                $this->config->password() ?? ''
            )
            ->post($this->endpoint('leads/add'), $data);

        ApiNotification::query()
            ->create([
                'provider' => 'motordat-crm',
                'processed' => $this->response->successful(),
                'data_model' => $data_model,
                'reference' => $reference,
                'data' => [
                    'request' => $data,
                    'result' => $this->response->json(),
                ],
                'headers' => [
                    'Vendor-Name' => $this->config->vendorName()
                ],
            ]);

        return $this->response->successful();
    }

    /**
     * Gets the response message from the last API call.
     */
    public function getResponseMessage(): ?string
    {
        return $this->response->json('Message')
            ?? $this->response->json('message')
            ?? $this->response->json('error')
            ?? null;
    }

    private function mapEnquiry(EnquiryModel $enquiry, EnquiryActionModel $action): array
    {
        $params = $this->convertEnquiryWithObjectsToParams($enquiry, 'motordat-field');

        $vehicle = $enquiry->objects->where('object_type', 'vehicle')->first()?->object;
        $type = $vehicle && !$vehicle->is_new ? 'USED' : 'NEW';

        $params['dealer_code'] = $this->getDealerCode($enquiry, $action);
        $params['enquiry_type'] = $type;
        $params['lead_type'] = $enquiry->type?->name;
        $params['lead_source'] = 'ENQ-' . $enquiry->id;

        return [
            'Customer' => $this->buildCustomerData($params),
            'Enquiry' => $this->buildEnquiryData($params),
            'Appraisal' => $this->buildAppraisalData($params),
        ];
    }

    private function mapReservationData(Reservation $reservation): array
    {
        if (!empty($reservation->first_name)) {
            $firstName = $reservation->first_name;
            $lastName = $reservation->last_name ?? '';
        } else {
            $nameParts = explode(' ', $reservation->name ?? '');
            $firstName = array_shift($nameParts);
            $lastName = implode(' ', $nameParts);
        }

        $dealerCode = $reservation->vehicle?->dealership?->data['motordat-crm-dealer-code']
            ?? Settings::get('motordat-crm-fallback-dealer-code')
            ?? '';

        $params = [
            'first_name' => $firstName,
            'last_name' => $lastName,
            'tel' => $reservation->contact_number,
            'email' => $reservation->email,
            'preferredContactMethods' => 'sales_contact,email',

            'enquiry_type' => $reservation->vehicle?->is_new ? 'NEW' : 'USED',
            'notes' => __('crm.reservation_details', [
                'amount' => $reservation->amount,
                'confirmed_at' => $reservation->confirmed_at?->format('Y-m-d H:i:s'),
                'payment_reference' => $reservation->invoice?->payments()->pluck('reference')->implode(','),
            ]),
            'dealer_code' => $dealerCode,
            'lead_type' => 'Vehicle reservation',
            'lead_source' => 'RES-' . $reservation->id,

            'vehicle_type' => $reservation->vehicle?->is_new ? 'New' : 'Used',
            'vehicle_make' => $reservation->vehicle?->make?->name ?? '',
            'vehicle_model' => $reservation->vehicle?->model?->name ?? '',
            'vehicle_vrm' => $reservation->vehicle?->registration_number ?? '',
            'vehicle_derivative' => $reservation->vehicle?->derivative ?? '',
            'vehicle_cap_id' => $reservation->vehicle?->cap_id ?? '',
        ];

        return [
            'Customer' => $this->buildCustomerData($params),
            'Enquiry' => $this->buildEnquiryData($params),
            'Appraisal' => $this->buildAppraisalData($params),
        ];
    }

    private function mapServiceBooking(Booking $booking): array
    {
        $dealerCode = $booking->location?->data['motordat-crm-dealer-code']
            ?? Settings::get('motordat-crm-fallback-dealer-code')
            ?? '';

        $params = [
            'first_name' => $booking->first_name,
            'last_name' => $booking->last_name,
            'tel' => $booking->phone_number,
            'email' => $booking->email,
            'preferredContactMethods' => 'sales_contact,email',

            'vehicle_type' => 'Used',
            'vehicle_make' => $booking->vehicle_data['make'] ?? '',
            'vehicle_model' => $booking->vehicle_data['model'] ?? '',
            'vehicle_vrm' => $booking->registration_number ?? '',
            'vehicle_derivative' => $booking->vehicle_data['derivative'] ?? '',

            'enquiry_type' => 'USED',
            'notes' => __('crm.service_booking_details', [
                'time' => $booking->booking_time,
                'packages' => $booking->packages()->pluck('name')->implode(', '),
                'details' => collect($booking->data ?? [])
                    ->map(fn ($value, $key) => "$key: $value")
                    ->implode(', '),
                'notes' => $booking->notes,
            ]),
            'dealer_code' => $dealerCode,
            'lead_type' => 'Service booking',
            'lead_source' => 'SRV-' . $booking->id,
        ];

        return [
            'Customer' => $this->buildCustomerData($params),
            'Enquiry' => $this->buildEnquiryData($params),
            'Appraisal' => $this->buildAppraisalData($params),
        ];
    }

    private function buildCustomerData(array $params): array
    {
        $methods = $this->parseContactMethods($params);

        return [
            'title' => $this->truncate($this->sanitize($params['title'] ?? ''), 20),
            'first_name' => $this->truncate($this->sanitize($params['first_name'] ?? ''), 50),
            'last_name' => $this->truncate($this->sanitize($params['last_name'] ?? ''), 50),
            'tel' => $this->truncate($this->sanitize($params['tel'] ?? ''), 18),
            'home' => $this->truncate($this->sanitize($params['home'] ?? ''), 20),
            'office' => $this->truncate($this->sanitize($params['office'] ?? ''), 20),
            'email' => $this->truncate($this->sanitize($params['email'] ?? ''), 100),
            'address1' => $this->sanitize($params['address1'] ?? ''),
            'address2' => ($params['address2'] ?? null) ? $this->sanitize($params['address2']) : null,
            'address3' => ($params['address3'] ?? null) ? $this->sanitize($params['address3']) : null,
            'address4' => ($params['address4'] ?? null) ? $this->sanitize($params['address4']) : null,
            'Town' => ($params['town'] ?? null) ? $this->sanitize($params['town']) : null,
            'County' => ($params['county'] ?? null) ? $this->sanitize($params['county']) : null,
            'postcode' => $this->truncate($this->sanitize($params['postcode'] ?? ''), 10),
            'country' => $this->sanitize($params['country'] ?? 'United Kingdom'),
            'phone_allowed' => $this->toBooleanString(in_array('sales_contact', $methods)),
            'mail_allowed' => $this->toBooleanString(in_array('mail', $methods)),
            'email_allowed' => $this->toBooleanString(in_array('email', $methods)),
            'sms_allowed' => $this->toBooleanString(in_array('sms', $methods)),
        ];
    }

    private function buildEnquiryData(array $params): array
    {
        return [
            'type' => $this->truncate($params['enquiry_type'] ?? 'NEW', 20),
            'notes' => $this->sanitize($params['notes'] ?? ''),
            'dealerCode' => $this->truncate($this->sanitize($params['dealer_code'] ?? ''), 10),
            'leadType' => $this->truncate($this->sanitize($params['lead_type'] ?? 'Sales'), 20),
            'currentMgOwner' => isset($params['currentMgOwner'])
                ? $this->truncate($this->sanitize($params['currentMgOwner']), 20)
                : null,
            'currentVehicleModel' => isset($params['currentVehicleModel'])
                ? $this->truncate($this->sanitize($params['currentVehicleModel']), 20)
                : null,
            'countryOfPurchase' => isset($params['countryOfPurchase'])
                ? $this->truncate($this->sanitize($params['countryOfPurchase']), 20)
                : null,
            'currentVehicleRegistrationNumber' => isset($params['vehicle_vrm'])
                ? $this->truncate($this->sanitize($params['vehicle_vrm']), 10)
                : null,
            'origin' => $this->truncate($this->sanitize($params['origin'] ?? 'Website'), 20),
            'leadSource' => $this->truncate($this->sanitize($params['lead_source'] ?? 'Unknown'), 20),
            'signup_terms' => $this->truncate($this->sanitize($params['signup_terms'] ?? ''), 50),
            'utm_source' => $this->sanitize($params['utm_source'] ?? ''),
            'utm_campaign' => $this->sanitize($params['utm_campaign'] ?? ''),
            'utm_medium' => $this->sanitize($params['utm_medium'] ?? ''),
            'driftrock_id' => $this->sanitize($params['driftrock_id'] ?? ''),
            'approved' => $this->sanitize($params['approved'] ?? ''),
            'partner' => $this->sanitize($params['partner'] ?? ''),
        ];
    }

    private function buildAppraisalData(array $params): array
    {
        return [
            'type' => $params['vehicle_type'] ?? 'New',
            'Make' => $this->truncate($this->sanitize($params['vehicle_make'] ?? ''), 100),
            'Model' => $this->truncate($this->sanitize($params['vehicle_model'] ?? ''), 100),
            'registration' => $this->truncate($this->sanitize($params['vehicle_vrm'] ?? ''), 20),
            'Derivative' => $this->truncate($this->sanitize($params['vehicle_derivative'] ?? ''), 75),
            'CapID' => $this->truncate($this->sanitize($params['vehicle_cap_id'] ?? ''), 10),
            'drivetrain_config' => '',
            'paint_config' => '',
            'Roof_config' => '',
            'Interior_config' => '',
            'partExchangeMake' => $this->truncate($this->sanitize($params['part_ex_make'] ?? ''), 100),
            'partExchangeModel' => $this->truncate($this->sanitize($params['part_ex_model'] ?? ''), 100),
            'partExchangeRegistrationNumber' => $this->truncate($this->sanitize($params['part_ex_vrm'] ?? ''), 10),
            'partExchangeMileage' => isset($params['part_ex_mileage']) && $params['part_ex_mileage']
                ? (string)(int)$params['part_ex_mileage']
                : '',
        ];
    }

    protected function mapVehicle(Vehicle $vehicle): array
    {
        return [
            'vehicle_type' => $vehicle->is_new ? 'New' : 'Used',
            'vehicle_make' => $vehicle->make?->name,
            'vehicle_model' => $vehicle->model?->name,
            'vehicle_vrm' => $vehicle->vrm_condensed,
            'vehicle_derivative' => $vehicle->derivative,
            'vehicle_cap_id' => $vehicle->cap_id,
        ];
    }

    protected function mapOffer(VehicleOffer $offer): array
    {
        return [
            'vehicle_type' => 'New',
            'vehicle_make' => $offer->make?->name,
            'vehicle_model' => $offer->model?->name,
            'vehicle_vrm' => '',
            'vehicle_derivative' => 'Offer: ' . $offer->name,
        ];
    }

    protected function mapNewCar(NewCar $newCar): array
    {
        return [
            'vehicle_type' => 'New',
            'vehicle_make' => $newCar->make?->name,
            'vehicle_model' => $newCar->model?->name,
            'vehicle_vrm' => '',
            'vehicle_derivative' => 'New Vehicle: ' . $newCar->name,
        ];
    }

    protected function mapValuation(VehicleValuation $valuation): array
    {
        return [
            'part_ex_make' => $valuation->make,
            'part_ex_model' => $valuation->model,
            'part_ex_vrm' => $valuation->registration,
            'part_ex_mileage' => $valuation->mileage,
        ];
    }

    private function getDealerCode(EnquiryModel $enquiry, EnquiryActionModel $action): string
    {
        $dealerCode = null;

        $dealership = $enquiry->objects->where('object_type', 'dealership')->first()?->object;
        if ($dealership) {
            $dealerCode = $dealership->data['motordat-crm-dealer-code'] ?? null;
        }

        if (empty($dealerCode) && $vehicle = $enquiry->objects->where('object_type', 'vehicle')->first()?->object) {
            $dealerCode = $vehicle->dealership?->data['motordat-crm-dealer-code'] ?? null;
        }

        if (empty($dealerCode)) {
            $dealerCode = Settings::get('motordat-crm-fallback-dealer-code');
        }

        return $dealerCode ?? '';
    }

    /**
     * Truncate string to max length
     */
    private function truncate(?string $value, int $maxLength): string
    {
        if ($value === null) {
            return '';
        }

        return mb_substr($value, 0, $maxLength);
    }

    /**
     * Sanitize string by removing disallowed characters
     * Allowed: !@#$%^&*()/-*+
     * Disallowed: "|\'{} and unicode characters
     */
    private function sanitize(?string $value): string
    {
        if ($value === null) {
            return '';
        }

        // Remove disallowed characters: "|\'{} and any non-ASCII unicode
        $sanitized = preg_replace('/["\|\\\'\{\}]/', '', $value);

        // Replace unicode characters with ASCII equivalents where possible
        $sanitized = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $sanitized ?? '');

        return $sanitized ?: '';
    }

    /**
     * Convert value to a boolean string ("1" or "0")
     */
    private function toBooleanString(mixed $value): string
    {
        if (is_string($value)) {
            return in_array(strtolower($value), ['1', 'true', 'yes']) ? '1' : '0';
        }

        return $value ? '1' : '0';
    }
}
