<?php

namespace Tests\Tenant;

use App\Crm\Config\BrevoConfig;
use App\Facades\Settings;
use App\Services\BrevoApi;
use Brevo\Client\Api\ContactsApi;
use Database\Seeders\Tenant\BrevoSettingSeeder;
use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use GuzzleHttp\Psr7\Response;
use Illuminate\Support\Facades\Log;
use Mtc\Crm\Facades\Enquiries;
use Mtc\Crm\Models\Enquiry;
use Mtc\Crm\Models\EnquiryAction;
use Tests\TenantTestCase;
use Mockery;

class SendToBrevoTest extends TenantTestCase
{
    public function testSendToBrevo()
    {
        $history = [];
        $historyMiddleware = Middleware::history($history);

        // Mock responses for Guzzle
        $mock = new MockHandler([
            new Response(200),
            new Response(400, [], json_encode([
                'code' => 'duplicate_parameter',
                'message' => 'email is already associated with another Contact'
            ])),
        ]);

        $handlerStack = HandlerStack::create($mock);
        $handlerStack->push($historyMiddleware);
        $mockHttpClient = new Client(['handler' => $handlerStack]);
        $mockConfig = Mockery::mock(BrevoConfig::class);
        $mockConfig->shouldReceive('apiKey')
            ->andReturn('test-api-key');

        $this->app->bind(BrevoApi::class, function () use ($mockConfig, $mockHttpClient) {
            return new BrevoApi($mockConfig, $mockHttpClient);
        });

        $enquiry = Enquiry::factory()->create([
                'email' => 'test@example.com',
                'details' => []
            ]
        );

        $action = EnquiryAction::factory()->create([
            'action_name' => 'send-to-brevo',
            'data' => [
                'brevo_list_id' => 12345,
                'brevo_update_enabled' => true,
            ],
            'processed_at' => null,
            'failed_at' => null
        ]);

        $brevo = Enquiries::makeAction('send-to-brevo');

        //Successful request
        $response = $brevo->handle($action, $enquiry);
        $this->assertCount(1, $history);
        $request = $history[0]['request'];
        $this->assertEquals('POST', $request->getMethod());
        $this->assertEquals('https://api.brevo.com/v3/contacts', (string)$request->getUri());
        $this->assertTrue($response);
        $this->assertEquals(
            'Contact with email test@example.com has been successfully created and added to the following list(s): 12345.',
            $brevo->successDetails()
        );

        //Failed request
        $response = $brevo->handle($action, $enquiry);
        $this->assertCount(2, $history);
        $request = $history[1]['request'];
        $this->assertEquals('POST', $request->getMethod());
        $this->assertEquals('https://api.brevo.com/v3/contacts', (string)$request->getUri());
        $this->assertFalse($response);
        $this->assertEquals(
            'email is already associated with another Contact',
            $brevo->failureReason()
        );
    }

    public function testGetFormAdditionalDataFields()
    {
        $fields = Enquiries::makeAction('send-to-brevo')->formAdditionalDataFields();
        $this->assertIsArray($fields);
        $this->assertArrayHasKey('brevo_list_id', $fields);
        $this->assertArrayHasKey('brevo_update_enabled', $fields);
    }

    public function testApiKeyReturnsCorrectValue()
    {
        $this->seed(BrevoSettingSeeder::class);
        Settings::update('brevo-api-key', 'foo-baz');
        $config = new BrevoConfig();
        $this->assertEquals('foo-baz', $config->apiKey());
    }

    public function testGenericExceptionAndErrorLogging()
    {
        Log::shouldReceive('error')
            ->once()
            ->with(Mockery::on(function ($message) {
                return str_contains($message, 'Exception: Something went wrong');
            }));

        $mockContactsApi = Mockery::mock(ContactsApi::class);
        $mockContactsApi->shouldReceive('createContact')
            ->andThrow(new \Exception('Something went wrong'));

        $mockConfig = Mockery::mock(BrevoConfig::class);
        $mockConfig->shouldReceive('apiKey')->andReturn('test-api-key');

        $brevoApi = new BrevoApi($mockConfig);

        // Inject mocked instance
        $reflection = new \ReflectionClass($brevoApi);
        $property = $reflection->getProperty('api_instance');
        $property->setAccessible(true);
        $property->setValue($brevoApi, $mockContactsApi);

        $enquiry = Enquiry::factory()->create(['email' => 'foo@example.com']);
        $action = EnquiryAction::factory()->create([
            'data' => ['brevo_list_id' => 123],
        ]);

        $result = $brevoApi->addCustomerToBrevo($enquiry, $action);

        $this->assertFalse($result);
        $this->assertNull($brevoApi->getResponseMessage());
    }

    public function testGetEnquiryParamsWithVehicleModel()
    {
        $vehicle = \Mtc\MercuryDataModels\Vehicle::factory()->create([
            'title' => 'Tesla Model X',
        ]);

        // create a fake FormQuestion that maps to a brevo-field
        $question = \Mtc\Crm\Models\FormQuestion::factory()->create([
            'data' => ['brevo-field' => 'VEHICLE_MODEL']
        ]);

        $enquiry = Enquiry::factory()->create([
            'reason_type' => 'vehicle',
            'email' => 'carlover@example.com',
            'details' => [
                $question->id => ['answer' => $vehicle->id],
            ]
        ]);

        EnquiryAction::factory()->create([
            'data' => ['brevo_list_id' => 123],
        ]);

        $mockConfig = Mockery::mock(BrevoConfig::class);
        $mockConfig->shouldReceive('apiKey')->andReturn('test-api-key');
        $brevoApi = new BrevoApi($mockConfig);

        $method = new \ReflectionMethod(BrevoApi::class, 'getEnquiryParams');
        $method->setAccessible(true);
        $params = $method->invoke($brevoApi, $enquiry);

        $this->assertArrayHasKey('VEHICLE_MODEL', $params);
        $this->assertEquals('Tesla Model X', $params['VEHICLE_MODEL']);
    }

    public function testNonVehicleFieldReturnsCorrectAnswer()
    {
        // a brevo field (NON VEHICLE_MODEL)
        $question = \Mtc\Crm\Models\FormQuestion::factory()->create([
            'data' => ['brevo-field' => 'FIRSTNAME'],
        ]);

        $enquiry = Enquiry::factory()->create([
            'reason_type' => 'finance', // not 'vehicle'
            'email' => 'user@example.com',
            'details' => [
                $question->id => ['answer' => 123]
            ]
        ]);

        $action = \Mtc\Crm\Models\EnquiryAction::factory()->create([
            'action_name' => 'send-to-brevo',
            'data' => [
                'brevo_list_id' => 98765,
                'brevo_update_enabled' => true,
            ]
        ]);

        $mockHttpClient = new \GuzzleHttp\Client(['handler' => \GuzzleHttp\HandlerStack::create(new \GuzzleHttp\Handler\MockHandler([
            new \GuzzleHttp\Psr7\Response(200),
        ]))]);

        $mockConfig = Mockery::mock(\App\Crm\Config\BrevoConfig::class);
        $mockConfig->shouldReceive('apiKey')->andReturn('fake-api-key');

        $brevoApi = new \App\Services\BrevoApi($mockConfig, $mockHttpClient);
        $brevoApi->addCustomerToBrevo($enquiry, $action);

        $this->assertStringContainsString('user@example.com', $brevoApi->getResponseMessage());
    }
}
