<?php

namespace App\VehicleSpec\Services;

use App\VehicleSpec\Config\HPIConfig;
use App\VehicleSpec\Contracts\VehicleSpecData;
use App\VehicleSpec\Contracts\VehicleSpecProvider;
use App\VehicleSpec\Contracts\VehicleStandardEquipmentItem;
use App\VehicleSpec\Contracts\VehicleTechnicalDataItem;
use App\VehicleSpec\Maps\HPITechnicalFieldMap;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Mtc\MercuryDataModels\ApiNotification;
use Mtc\MercuryDataModels\Vehicle;
use Throwable;

class HPISpecProvider implements VehicleSpecProvider
{
    protected static $endpoint_live = 'https://wss.hpi.co.uk/TradeSoap/services/SupplementaryEnquiryV1/';
    protected static $endpoint_test = 'http://pat-wss.hpi.co.uk/TradeSoap/services/SupplementaryEnquiryV1/';

    public function __construct(protected readonly HPIConfig $config)
    {
        //
    }

    public function getSpec(Vehicle $vehicle): VehicleSpecData
    {
        $spec = new VehicleSpecData();
        $spec->technical_data = collect();
        $spec->standard_equipment = collect();

        $request = $this->getVehicleRequest($vehicle);
        $responseBody = null;

        try {
            $response = $this->call($request);
            $responseBody = (string) $response->body();
            $xml = simplexml_load_string($responseBody);

            $results = $xml
                ->children('http://schemas.xmlsoap.org/soap/envelope/')
                ->Body
                ->children('http://webservices.hpi.co.uk/SupplementaryEnquiryV1')
                ->EnquiryResponse
                ->RequestResults;

            $spec->technical_data = $this->getTechnicalDataFromXml($results);
            $spec->standard_equipment = $this->getStandardEquipmentFromXml($results);

            Cache::remember(
                'HPI_stored_response_' . tenant()->id . '_' . $vehicle->vrm_condensed,
                10,
                static fn() => $response->body()
            );
        } catch (Throwable $exception) {
            Log::error(self::class . '::' . __FUNCTION__ . '() ' . $exception->getMessage());
        }

        $this->logApiNotification($vehicle, $request, $responseBody, $spec);

        return $spec;
    }

    private function logApiNotification(
        Vehicle $vehicle,
        array $request,
        ?string $responseBody,
        VehicleSpecData $spec,
    ): void {
        // Redact password: api_notifications is admin-readable
        $request['subscriber_details']['password'] = '[ELIDED]';

        ApiNotification::query()->create([
            'provider' => 'hpi',
            'reference' => $vehicle->vrm_condensed,
            // HPI returns SOAP Faults inside HTTP 200 responses; HTTP success alone
            // does not indicate a usable result, so processed reflects data presence.
            'processed' => $spec->standard_equipment->isNotEmpty()
                || $spec->technical_data->isNotEmpty(),
            'data' => [
                'request' => $request,
                'result' => $responseBody,
                'endpoint' => $this->endpoint(),
            ],
            'data_model' => Vehicle::class,
        ]);
    }

    protected function call(array $request_data)
    {
        $response = Http::withHeaders($this->getHeaders())
            ->timeout(20)
            ->withBody($this->getEnvelope($request_data), 'text/xml')
            ->post($this->endpoint());

        if ($response->failed()) {
            Log::error('HPI call failed', [
                'endpoint' => $this->endpoint(),
                'request_data' => $request_data,
                'status_code' => $response->status(),
                'response' => $response->body(),
            ]);
            throw new \Exception('Failed to perform call to ' . $this->endpoint());
        }

        return $response;
    }

    protected function getEnvelope(array $request_data)
    {
        return view('hpi_enquiry_request', ['data' => $request_data])->render();
    }

    protected function endpoint()
    {
        return app()->environment() === 'production'
            ? self::$endpoint_live
            : self::$endpoint_test;
    }

    protected function getVehicleRequest(Vehicle $vehicle): array
    {
        return [
            'subscriber_details' => [
                'customer_code' => $this->config->customerCode(),
                'initials' => $this->config->initials(),
                'password' => $this->config->password(),
            ],
            'asset' => [
                'vrm' => $vehicle->vrm_condensed,
                'vin' => $vehicle->vin,
                'mileage' => $vehicle->odometer_mi,
                'reference' => '',
            ],
            'primary_product' => $this->config->primaryProductCode(),
            'supplementary_product' => $this->config->supplementaryProductCode(),
        ];
    }

    protected function getHeaders(): array
    {
        return [
            'Content-Type' => 'text/xml; charset=utf-8',
            'SOAPAction' => 'http://webservices.hpi.co.uk/SupplementaryEnquiryV1/enquire',
        ];
    }

    protected function getTechnicalDataFromXml(\SimpleXMLElement $element): Collection
    {
        return collect(HPITechnicalFieldMap::all())
            ->filter(fn($field) => !empty(data_get($element, $field['value'])))
            ->map(fn($field) => new VehicleTechnicalDataItem(
                data_get($element, $field['code'] ?? null),
                $field['category'],
                $field['description'],
                data_get($element, $field['value']),
            ));
    }

    protected function getStandardEquipmentFromXml(\SimpleXMLElement $element): Collection
    {
        $details = data_get(
            json_decode(json_encode($element), true),
            'Asset.SupplementaryAssetData.OptionListResponse.OptionList.ModelOptionDetail',
            []
        );

        return collect($details)
            ->map(fn($item) => new VehicleStandardEquipmentItem(
                data_get($item, 'Code') ?: '',
                data_get($item, 'ShortDescription'),
                data_get($item, 'ShortDescription'),
                data_get($item, 'OptionKind')
            ));
    }
}
