<?php

declare(strict_types=1);

namespace Tests\Tenant\Integrations\CRM;

use App\Crm\SendToMotordat;
use App\Facades\Settings;
use Database\Seeders\Tenant\MotordatCRMSettingSeeder;
use Illuminate\Http\Client\Request;
use Illuminate\Support\Facades\Http;
use Mtc\Crm\Facades\Enquiries;
use Mtc\Crm\Models\EnquiryAction;
use Mtc\Crm\Models\EnquiryObject;
use Mtc\Crm\Models\FormQuestion;
use Mtc\MercuryDataModels\Dealership;
use Mtc\MercuryDataModels\Enquiry;
use Mtc\MercuryDataModels\FuelType;
use Mtc\MercuryDataModels\Vehicle;
use Mtc\MercuryDataModels\VehicleMake;
use Mtc\MercuryDataModels\VehicleModel;
use Mtc\MercuryDataModels\VehicleOffer;
use Mtc\MercuryDataModels\VehicleValuation;
use Tests\TenantTestCase;

/**
 * Motordat CRM Integration Tests
 *
 * Tests the SendToMotordat CRM action and MotordatCrmApi service.
 * Covers lead sending, field mapping, validation, error handling, and configuration.
 *
 * @see \App\Crm\SendToMotordat
 * @see \App\Services\MotordatCrmApi
 */
class MotordatCrmTest extends TenantTestCase
{
    private const API_ENDPOINT = 'https://oemapi.motordat.com/leads/add';

    protected function setUp(): void
    {
        parent::setUp();

        Http::preventStrayRequests();
    }

    // =========================================================================
    // Lead Sending Tests
    // =========================================================================

    public function test_sends_lead_successfully_with_correct_headers(): void
    {
        $this->seedMotordatSettings();

        $enquiry = Enquiry::factory()->create();
        $action = $this->createMotordatEnquiryAction();

        Http::fake([
            self::API_ENDPOINT => Http::response(
                ['Message' => 'Enquiry has been created successfully'],
                201
            ),
        ]);

        $result = Enquiries::makeAction('send-to-motordat')->handle($action, $enquiry);

        $this->assertTrue($result);

        Http::assertSent(function (Request $request) {
            return $request->url() === self::API_ENDPOINT
                && $request->hasHeader('Vendor-Name', 'TestVendor')
                && $request->hasHeader('Authorization')
                && $request->hasHeader('Content-Type', 'application/json');
        });
    }

    public function test_sends_lead_with_used_vehicle_data(): void
    {
        $this->seedMotordatSettings();

        $make = VehicleMake::factory()->create(['name' => 'Ford']);
        $model = VehicleModel::factory()->create(['name' => 'Focus', 'make_id' => $make->id]);
        $fuelType = FuelType::factory()->create(['name' => 'Petrol']);
        $dealership = Dealership::factory()->create([
            'data' => ['motordat-crm-dealer-code' => 'DEAL123'],
        ]);

        $vehicle = Vehicle::factory()->create([
            'make_id' => $make->id,
            'model_id' => $model->id,
            'fuel_type_id' => $fuelType->id,
            'dealership_id' => $dealership->id,
            'derivative' => '1.6 Zetec',
            'registration_number' => 'AB12 CDE',
            'cap_id' => '123456',
            'price' => 15000,
            'is_new' => false,
        ]);

        $enquiry = Enquiry::factory()->create([
            'reason_id' => $vehicle->id,
            'reason_type' => 'vehicle',
        ]);

        EnquiryObject::factory()->create([
            'enquiry_id' => $enquiry->id,
            'object_id' => $vehicle->id,
            'object_type' => 'vehicle',
        ]);

        $action = $this->createMotordatEnquiryAction();

        Http::fake([self::API_ENDPOINT => Http::response([], 201)]);

        $result = Enquiries::makeAction('send-to-motordat')->handle($action, $enquiry);

        $this->assertTrue($result);

        Http::assertSent(function (Request $request) {
            $data = $request->data();

            return $data['Appraisal']['Make'] === 'Ford'
                && $data['Appraisal']['Model'] === 'Focus'
                && $data['Appraisal']['Derivative'] === '1.6 Zetec'
                && $data['Appraisal']['registration'] === 'AB12CDE'
                && $data['Appraisal']['type'] === 'Used'
                && $data['Enquiry']['type'] === 'USED'
                && $data['Enquiry']['dealerCode'] === 'DEAL123';
        });
    }

    public function test_sends_lead_with_new_vehicle_data(): void
    {
        $this->seedMotordatSettings();

        $make = VehicleMake::factory()->create(['name' => 'MG']);
        $model = VehicleModel::factory()->create(['name' => 'ZS', 'make_id' => $make->id]);

        $vehicle = Vehicle::factory()->create([
            'make_id' => $make->id,
            'model_id' => $model->id,
            'is_new' => true,
        ]);

        $enquiry = Enquiry::factory()->create([
            'reason_id' => $vehicle->id,
            'reason_type' => 'vehicle',
        ]);

        EnquiryObject::factory()->create([
            'enquiry_id' => $enquiry->id,
            'object_id' => $vehicle->id,
            'object_type' => 'vehicle',
        ]);

        $action = $this->createMotordatEnquiryAction();

        Http::fake([self::API_ENDPOINT => Http::response([], 201)]);

        Enquiries::makeAction('send-to-motordat')->handle($action, $enquiry);

        Http::assertSent(function (Request $request) {
            $data = $request->data();

            return $data['Enquiry']['type'] === 'NEW'
                && $data['Appraisal']['type'] === 'New';
        });
    }

    public function test_sends_lead_with_offer_data(): void
    {
        $this->seedMotordatSettings();

        $make = VehicleMake::factory()->create(['name' => 'MG']);
        $model = VehicleModel::factory()->create(['name' => 'HS', 'make_id' => $make->id]);

        $offer = VehicleOffer::factory()->create([
            'make_id' => $make->id,
            'model_id' => $model->id,
            'name' => 'Summer Sale',
        ]);

        $enquiry = Enquiry::factory()->create([
            'reason_id' => $offer->id,
            'reason_type' => 'offer',
        ]);

        EnquiryObject::factory()->create([
            'enquiry_id' => $enquiry->id,
            'object_id' => $offer->id,
            'object_type' => 'offer',
        ]);

        $action = $this->createMotordatEnquiryAction();

        Http::fake([self::API_ENDPOINT => Http::response([], 201)]);

        Enquiries::makeAction('send-to-motordat')->handle($action, $enquiry);

        Http::assertSent(function (Request $request) {
            $data = $request->data();

            return $data['Enquiry']['type'] === 'NEW'
                && $data['Appraisal']['Make'] === 'MG'
                && $data['Appraisal']['Model'] === 'HS'
                && $data['Appraisal']['Derivative'] === 'Offer: Summer Sale';
        });
    }

    public function test_sends_lead_with_part_exchange_valuation(): void
    {
        $this->seedMotordatSettings();

        $valuation = VehicleValuation::factory()->create([
            'make' => 'BMW',
            'model' => '3 Series',
            'registration' => 'XY19 ABC',
            'mileage' => 45000,
        ]);

        $enquiry = Enquiry::factory()->create([
            'valuation_id' => $valuation->id,
        ]);

        EnquiryObject::factory()->create([
            'enquiry_id' => $enquiry->id,
            'object_id' => $valuation->id,
            'object_type' => 'valuation',
        ]);

        $action = $this->createMotordatEnquiryAction();

        Http::fake([self::API_ENDPOINT => Http::response([], 201)]);

        Enquiries::makeAction('send-to-motordat')->handle($action, $enquiry);

        Http::assertSent(function (Request $request) {
            $data = $request->data();

            return $data['Appraisal']['partExchangeMake'] === 'BMW'
                && $data['Appraisal']['partExchangeModel'] === '3 Series'
                && $data['Appraisal']['partExchangeRegistrationNumber'] === 'XY19 ABC'
                && $data['Appraisal']['partExchangeMileage'] === '45000';
        });
    }

    // =========================================================================
    // Customer Field Mapping Tests
    // =========================================================================

    public function test_maps_customer_fields_from_form_questions(): void
    {
        $this->seedMotordatSettings();

        $titleQuestion = FormQuestion::factory()->create(['data' => ['motordat-field' => 'title']]);
        $firstNameQuestion = FormQuestion::factory()->create(['data' => ['motordat-field' => 'first_name']]);
        $lastNameQuestion = FormQuestion::factory()->create(['data' => ['motordat-field' => 'last_name']]);
        $emailQuestion = FormQuestion::factory()->create(['data' => ['motordat-field' => 'email']]);
        $telQuestion = FormQuestion::factory()->create(['data' => ['motordat-field' => 'tel']]);
        $postcodeQuestion = FormQuestion::factory()->create(['data' => ['motordat-field' => 'postcode']]);

        $enquiry = Enquiry::factory()->create([
            'details' => [
                $titleQuestion->id => ['answer' => 'Mr'],
                $firstNameQuestion->id => ['answer' => 'John'],
                $lastNameQuestion->id => ['answer' => 'Doe'],
                $emailQuestion->id => ['answer' => 'john.doe@example.com'],
                $telQuestion->id => ['answer' => '07123456789'],
                $postcodeQuestion->id => ['answer' => 'AB12 3CD'],
            ],
        ]);

        $action = $this->createMotordatEnquiryAction();

        Http::fake([self::API_ENDPOINT => Http::response([], 201)]);

        Enquiries::makeAction('send-to-motordat')->handle($action, $enquiry);

        Http::assertSent(function (Request $request) {
            $customer = $request->data()['Customer'];

            return $customer['title'] === 'Mr'
                && $customer['first_name'] === 'John'
                && $customer['last_name'] === 'Doe'
                && $customer['email'] === 'john.doe@example.com'
                && $customer['tel'] === '07123456789'
                && $customer['postcode'] === 'AB12 3CD';
        });
    }

    public function test_maps_enquiry_fields_from_form_questions(): void
    {
        $this->seedMotordatSettings();

        $notesQuestion = FormQuestion::factory()->create(['data' => ['motordat-field' => 'notes']]);
        $originQuestion = FormQuestion::factory()->create(['data' => ['motordat-field' => 'origin']]);

        $enquiry = Enquiry::factory()->create([
            'details' => [
                $notesQuestion->id => ['answer' => 'Interested in test drive'],
                $originQuestion->id => ['answer' => 'Website Form'],
            ],
        ]);

        $action = $this->createMotordatEnquiryAction();

        Http::fake([self::API_ENDPOINT => Http::response([], 201)]);

        Enquiries::makeAction('send-to-motordat')->handle($action, $enquiry);

        Http::assertSent(function (Request $request) {
            $enquiryData = $request->data()['Enquiry'];

            // leadType defaults to 'Sales' when enquiry->type is null
            return $enquiryData['leadType'] === 'Sales'
                && $enquiryData['notes'] === 'Interested in test drive'
                && $enquiryData['origin'] === 'Website Form';
        });
    }

    public function test_converts_preferred_contact_methods_to_marketing_permissions(): void
    {
        $this->seedMotordatSettings();

        $contactMethodsQuestion = FormQuestion::factory()->create([
            'data' => ['motordat-field' => 'preferredContactMethods'],
        ]);

        $enquiry = Enquiry::factory()->create([
            'details' => [
                $contactMethodsQuestion->id => ['answer' => ['email', 'sales_contact']],
            ],
        ]);

        $action = $this->createMotordatEnquiryAction();

        Http::fake([self::API_ENDPOINT => Http::response([], 201)]);

        Enquiries::makeAction('send-to-motordat')->handle($action, $enquiry);

        Http::assertSent(function (Request $request) {
            $customer = $request->data()['Customer'];

            // 'sales_contact' maps to phone_allowed, 'email' maps to email_allowed
            return $customer['phone_allowed'] === '1'
                && $customer['email_allowed'] === '1'
                && $customer['sms_allowed'] === '0'
                && $customer['mail_allowed'] === '0';
        });
    }

    // =========================================================================
    // Field Validation Tests
    // =========================================================================

    public function test_truncates_fields_to_maximum_allowed_length(): void
    {
        $this->seedMotordatSettings();

        $longFirstName = str_repeat('A', 100); // max 50
        $longLastName = str_repeat('B', 100);  // max 50
        $longDealerCode = str_repeat('X', 20); // max 10

        $firstNameQuestion = FormQuestion::factory()->create(['data' => ['motordat-field' => 'first_name']]);
        $lastNameQuestion = FormQuestion::factory()->create(['data' => ['motordat-field' => 'last_name']]);

        $dealership = Dealership::factory()->create([
            'data' => ['motordat-crm-dealer-code' => $longDealerCode],
        ]);

        $vehicle = Vehicle::factory()->create(['dealership_id' => $dealership->id]);

        $enquiry = Enquiry::factory()->create([
            'reason_id' => $vehicle->id,
            'reason_type' => 'vehicle',
            'details' => [
                $firstNameQuestion->id => ['answer' => $longFirstName],
                $lastNameQuestion->id => ['answer' => $longLastName],
            ],
        ]);

        EnquiryObject::factory()->create([
            'enquiry_id' => $enquiry->id,
            'object_id' => $vehicle->id,
            'object_type' => 'vehicle',
        ]);

        $action = $this->createMotordatEnquiryAction();

        Http::fake([self::API_ENDPOINT => Http::response([], 201)]);

        Enquiries::makeAction('send-to-motordat')->handle($action, $enquiry);

        Http::assertSent(function (Request $request) {
            $data = $request->data();

            return strlen($data['Customer']['first_name']) === 50
                && strlen($data['Customer']['last_name']) === 50
                && strlen($data['Enquiry']['dealerCode']) === 10;
        });
    }

    public function test_sanitizes_disallowed_characters_from_fields(): void
    {
        $this->seedMotordatSettings();

        $nameWithDisallowedChars = 'John"Doe|Test\'User{Name}';

        $firstNameQuestion = FormQuestion::factory()->create(['data' => ['motordat-field' => 'first_name']]);

        $enquiry = Enquiry::factory()->create([
            'details' => [
                $firstNameQuestion->id => ['answer' => $nameWithDisallowedChars],
            ],
        ]);

        $action = $this->createMotordatEnquiryAction();

        Http::fake([self::API_ENDPOINT => Http::response([], 201)]);

        Enquiries::makeAction('send-to-motordat')->handle($action, $enquiry);

        Http::assertSent(function (Request $request) {
            $firstName = $request->data()['Customer']['first_name'];

            // Characters "|\'{}  should be removed
            return strpos($firstName, '"') === false
                && strpos($firstName, '|') === false
                && strpos($firstName, "'") === false
                && strpos($firstName, '{') === false
                && strpos($firstName, '}') === false;
        });
    }

    // =========================================================================
    // Dealer Code Resolution Tests
    // =========================================================================

    public function test_uses_dealer_code_from_vehicle_dealership(): void
    {
        $this->seedMotordatSettings();

        $dealership = Dealership::factory()->create([
            'data' => ['motordat-crm-dealer-code' => 'VEH123'],
        ]);

        $vehicle = Vehicle::factory()->create(['dealership_id' => $dealership->id]);

        $enquiry = Enquiry::factory()->create([
            'reason_id' => $vehicle->id,
            'reason_type' => 'vehicle',
        ]);

        EnquiryObject::factory()->create([
            'enquiry_id' => $enquiry->id,
            'object_id' => $vehicle->id,
            'object_type' => 'vehicle',
        ]);

        $action = $this->createMotordatEnquiryAction();

        Http::fake([self::API_ENDPOINT => Http::response([], 201)]);

        Enquiries::makeAction('send-to-motordat')->handle($action, $enquiry);

        Http::assertSent(function (Request $request) {
            return $request->data()['Enquiry']['dealerCode'] === 'VEH123';
        });
    }

    public function test_uses_fallback_dealer_code_when_no_dealership(): void
    {
        $this->seedMotordatSettings();
        Settings::update('motordat-crm-fallback-dealer-code', 'FALLBACK');

        $enquiry = Enquiry::factory()->create();
        $action = $this->createMotordatEnquiryAction();

        Http::fake([self::API_ENDPOINT => Http::response([], 201)]);

        Enquiries::makeAction('send-to-motordat')->handle($action, $enquiry);

        Http::assertSent(function (Request $request) {
            return $request->data()['Enquiry']['dealerCode'] === 'FALLBACK';
        });
    }

    // =========================================================================
    // Error Handling Tests
    // =========================================================================

    public function test_returns_false_on_api_bad_request(): void
    {
        $this->seedMotordatSettings();

        $enquiry = Enquiry::factory()->create();
        $action = $this->createMotordatEnquiryAction();

        Http::fake([
            self::API_ENDPOINT => Http::response(['Message' => '400 Bad request'], 400),
        ]);

        $result = Enquiries::makeAction('send-to-motordat')->handle($action, $enquiry);

        $this->assertFalse($result);
    }

    public function test_returns_false_on_authentication_failure(): void
    {
        $this->seedMotordatSettings();

        $enquiry = Enquiry::factory()->create();
        $action = $this->createMotordatEnquiryAction();

        Http::fake([
            self::API_ENDPOINT => Http::response(['Message' => '401 Authorization Required'], 401),
        ]);

        $result = Enquiries::makeAction('send-to-motordat')->handle($action, $enquiry);

        $this->assertFalse($result);
    }

    public function test_skips_already_processed_action(): void
    {
        $this->seedMotordatSettings();

        $enquiry = Enquiry::factory()->create();
        $action = EnquiryAction::factory()->create([
            'action_name' => 'send-to-motordat',
            'processed_at' => now(), // Already processed
            'failed_at' => null,
        ]);

        Http::fake([self::API_ENDPOINT => Http::response([], 201)]);

        $result = Enquiries::makeAction('send-to-motordat')->handle($action, $enquiry);

        $this->assertNull($result);
        Http::assertNotSent(fn (Request $request) => $request->url() === self::API_ENDPOINT);
    }

    public function test_skips_action_with_max_attempts_exceeded(): void
    {
        $this->seedMotordatSettings();

        $enquiry = Enquiry::factory()->create();
        $action = EnquiryAction::factory()->create([
            'action_name' => 'send-to-motordat',
            'attempts' => 3, // Max attempts reached
            'processed_at' => null,
            'failed_at' => null,
        ]);

        Http::fake([self::API_ENDPOINT => Http::response([], 201)]);

        $result = Enquiries::makeAction('send-to-motordat')->handle($action, $enquiry);

        $this->assertNull($result);
        Http::assertNotSent(fn (Request $request) => $request->url() === self::API_ENDPOINT);
    }

    // =========================================================================
    // Integration Configuration Tests
    // =========================================================================

    public function test_integration_is_enabled_when_setting_is_true(): void
    {
        $this->seedMotordatSettings();

        $this->assertTrue(app(SendToMotordat::class)->enabled());
    }

    public function test_integration_is_disabled_when_setting_is_false(): void
    {
        $this->seed(MotordatCRMSettingSeeder::class);
        Settings::update('crm-motordat-enabled', false);

        $this->assertFalse(app(SendToMotordat::class)->enabled());
    }

    public function test_form_question_data_fields_includes_required_mappings(): void
    {
        $fields = app(SendToMotordat::class)->formQuestionDataFields();

        $this->assertArrayHasKey('motordat-field', $fields);
        $this->assertArrayHasKey('choices', $fields['motordat-field']);

        $choices = $fields['motordat-field']['choices'];

        $this->assertArrayHasKey('first_name', $choices);
        $this->assertArrayHasKey('last_name', $choices);
        $this->assertArrayHasKey('email', $choices);
        $this->assertArrayHasKey('tel', $choices);
        $this->assertArrayHasKey('notes', $choices);
        $this->assertArrayHasKey('preferredContactMethods', $choices);
    }

    public function test_dealership_additional_data_fields_includes_dealer_code(): void
    {
        $fields = app(SendToMotordat::class)->dealershipAdditionalDataFields();

        $this->assertArrayHasKey('motordat-crm-dealer-code', $fields);
        $this->assertEquals('text', $fields['motordat-crm-dealer-code']['type']);
    }

    public function test_form_additional_data_fields_returns_empty_array(): void
    {
        $fields = app(SendToMotordat::class)->formAdditionalDataFields();

        // Fallback dealer code is configured via Settings, not form fields
        $this->assertEmpty($fields);
    }

    // =========================================================================
    // Helper Methods
    // =========================================================================

    /**
     * Seed Motordat CRM settings with test configuration.
     */
    private function seedMotordatSettings(): void
    {
        $this->seed(MotordatCRMSettingSeeder::class);

        Settings::update('crm-motordat-enabled', true);
        Settings::update('crm-motordat-username', 'test-user');
        Settings::update('crm-motordat-password', 'test-password');
        Settings::update('crm-motordat-vendor-name', 'TestVendor');
    }

    /**
     * Create an EnquiryAction for Motordat CRM.
     */
    private function createMotordatEnquiryAction(array $overrides = []): EnquiryAction
    {
        return EnquiryAction::factory()->create(array_merge([
            'action_name' => 'send-to-motordat',
            'data' => [],
            'processed_at' => null,
            'failed_at' => null,
        ], $overrides));
    }
}
