<?php

namespace Tests\Tenant;

use App\Contracts\ForwardsReservationToCrm;
use App\Contracts\ForwardsServiceBookingToCrm;
use App\Events\ServiceBookingConfirmedEvent;
use App\Facades\Feature;
use App\Facades\Settings;
use App\Listeners\SendReservationToEnquiryIntegrations;
use App\Listeners\SendServiceBookingToEnquiryIntegrations;
use Carbon\Carbon;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;
use Mtc\Crm\Contracts\EnquiryAction;
use Mtc\Crm\Contracts\EnquiryActionModel;
use Mtc\Crm\Contracts\EnquiryModel;
use Mtc\MercuryDataModels\Booking;
use Mtc\MercuryDataModels\ProcessingLog;
use Mtc\MercuryDataModels\Vehicle;
use Mtc\VehicleReservations\Events\ReservationConfirmedEvent;
use Mtc\VehicleReservations\Jobs\ProcessIncomingReservationJob;
use Mtc\VehicleReservations\Reservation;
use Tests\TenantTestCase;
use Tests\UserForTenant;

class ProcessingLogTest extends TenantTestCase
{
    use UserForTenant;

    #[\Override]
    protected function setUp(): void
    {
        parent::setUp();
        Mail::fake();
    }

    public function testReservationNotificationStepsAreLogged()
    {
        Settings::make([
            'tab' => 'Automotive',
            'section' => 'Sales',
            'group' => 'Stripe',
            'name' => 'Confirmation recipient email address(es)',
            'config_key' => 'reservations-admin-team-emails',
            'type' => 'string',
            'value' => 'team@example.com',
        ]);

        $vehicle = Vehicle::factory()->create(['dealership_id' => null]);
        $reservation = Reservation::factory()->create([
            'vehicle_id' => $vehicle->id,
            'status' => 'confirmed',
            'email' => 'customer@example.com',
            'crm_reference' => null,
            'ingested_at' => null,
            'confirmation_sent_at' => null,
            'confirmed_at' => Carbon::now()->subMinutes(10),
        ]);

        new ProcessIncomingReservationJob($reservation)->handle();

        $logs = $reservation->processingLogs()->get();

        $customer = $logs->firstWhere('action', 'customer-notification');
        $this->assertNotNull($customer, 'Customer notification should be logged');
        $this->assertEquals(ProcessingLog::STATUS_SUCCESS, $customer->status);

        $team = $logs->firstWhere('action', 'team-notification');
        $this->assertNotNull($team, 'Team notification should be logged');
        $this->assertEquals(ProcessingLog::STATUS_SUCCESS, $team->status);
    }

    public function testCustomerNotificationLoggedAsSkippedWithoutEmail()
    {
        $vehicle = Vehicle::factory()->create(['dealership_id' => null]);
        $reservation = Reservation::factory()->create([
            'vehicle_id' => $vehicle->id,
            'status' => 'confirmed',
            'email' => null,
            'crm_reference' => null,
            'ingested_at' => null,
            'confirmation_sent_at' => null,
            'confirmed_at' => Carbon::now()->subMinutes(10),
        ]);

        new ProcessIncomingReservationJob($reservation)->handle();

        $customer = $reservation->processingLogs()->where('action', 'customer-notification')->first();
        $this->assertNotNull($customer);
        $this->assertEquals(ProcessingLog::STATUS_SKIPPED, $customer->status);
    }

    public function testCrmExportLogsEachProviderAndContinuesAfterFailure()
    {
        Config::set('crm.form_actions', [
            'failing-provider' => FailingReservationCrmStub::class,
            'succeeding-provider' => SucceedingReservationCrmStub::class,
        ]);

        $vehicle = Vehicle::factory()->create(['dealership_id' => null]);
        $reservation = Reservation::factory()->create([
            'vehicle_id' => $vehicle->id,
            'status' => 'confirmed',
            'crm_reference' => null,
        ]);

        new SendReservationToEnquiryIntegrations()->handle(new ReservationConfirmedEvent($reservation));

        $logs = $reservation->processingLogs()->where('action', 'crm-export')->get();
        $this->assertCount(2, $logs, 'Both providers should be attempted and logged');

        $failed = $logs->firstWhere('channel', 'failing-provider');
        $this->assertEquals(ProcessingLog::STATUS_FAILED, $failed->status);
        $this->assertEquals('boom', $failed->context);

        $succeeded = $logs->firstWhere('channel', 'succeeding-provider');
        $this->assertEquals(ProcessingLog::STATUS_SUCCESS, $succeeded->status);
    }

    public function testReservationExportSkippedWhenForwardingDisabled()
    {
        Settings::make([
            'tab' => 'CRM',
            'section' => 'Integrations',
            'group' => 'CRM Forwarding',
            'name' => 'Send reservations to CRM integrations',
            'config_key' => 'crm-forward-reservations-enabled',
            'type' => 'boolean',
            'value' => false,
        ]);
        Config::set('crm.form_actions', [
            'failing-provider' => FailingReservationCrmStub::class,
        ]);

        $vehicle = Vehicle::factory()->create(['dealership_id' => null]);
        $reservation = Reservation::factory()->create([
            'vehicle_id' => $vehicle->id,
            'status' => 'confirmed',
            'crm_reference' => null,
        ]);

        new SendReservationToEnquiryIntegrations()->handle(new ReservationConfirmedEvent($reservation));

        $logs = $reservation->processingLogs()->where('action', 'crm-export')->get();
        $this->assertCount(1, $logs, 'Only a single skipped log should be recorded, no provider attempts');
        $this->assertEquals(ProcessingLog::STATUS_SKIPPED, $logs->first()->status);
        $this->assertNull($logs->first()->channel);
    }

    public function testReservationExportSentWhenForwardingEnabled()
    {
        Settings::make([
            'tab' => 'CRM',
            'section' => 'Integrations',
            'group' => 'CRM Forwarding',
            'name' => 'Send reservations to CRM integrations',
            'config_key' => 'crm-forward-reservations-enabled',
            'type' => 'boolean',
            'value' => true,
        ]);
        Config::set('crm.form_actions', [
            'succeeding-provider' => SucceedingReservationCrmStub::class,
        ]);

        $vehicle = Vehicle::factory()->create(['dealership_id' => null]);
        $reservation = Reservation::factory()->create([
            'vehicle_id' => $vehicle->id,
            'status' => 'confirmed',
            'crm_reference' => null,
        ]);

        new SendReservationToEnquiryIntegrations()->handle(new ReservationConfirmedEvent($reservation));

        $log = $reservation->processingLogs()->where('action', 'crm-export')->first();
        $this->assertNotNull($log, 'Reservation should be forwarded when forwarding is enabled');
        $this->assertEquals(ProcessingLog::STATUS_SUCCESS, $log->status);
        $this->assertEquals('succeeding-provider', $log->channel);
    }

    public function testServiceBookingExportSkippedWhenForwardingDisabled()
    {
        Settings::make([
            'tab' => 'CRM',
            'section' => 'Integrations',
            'group' => 'CRM Forwarding',
            'name' => 'Send service bookings to CRM integrations',
            'config_key' => 'crm-forward-service-bookings-enabled',
            'type' => 'boolean',
            'value' => false,
        ]);
        Config::set('crm.form_actions', [
            'failing-provider' => FailingServiceBookingCrmStub::class,
        ]);

        $booking = Booking::factory()->create(['uuid' => Str::random(16)]);

        new SendServiceBookingToEnquiryIntegrations()->handle(new ServiceBookingConfirmedEvent($booking));

        $logs = $booking->processingLogs()->where('action', 'crm-export')->get();
        $this->assertCount(1, $logs, 'Only a single skipped log should be recorded, no provider attempts');
        $this->assertEquals(ProcessingLog::STATUS_SKIPPED, $logs->first()->status);
        $this->assertNull($logs->first()->channel);
    }

    public function testServiceBookingExportSentWhenForwardingEnabled()
    {
        Config::set('crm.form_actions', [
            'succeeding-provider' => SucceedingServiceBookingCrmStub::class,
        ]);

        $booking = Booking::factory()->create(['uuid' => Str::random(16)]);

        new SendServiceBookingToEnquiryIntegrations()->handle(new ServiceBookingConfirmedEvent($booking));

        $log = $booking->processingLogs()->where('action', 'crm-export')->first();
        $this->assertNotNull($log, 'Service booking should be forwarded when forwarding is enabled');
        $this->assertEquals(ProcessingLog::STATUS_SUCCESS, $log->status);
        $this->assertEquals('succeeding-provider', $log->channel);
    }

    public function testReservationViewIncludesProcessingLogs()
    {
        Feature::setEnabled(tenant(), ['reservations']);

        $vehicle = Vehicle::factory()->create();
        $reservation = Reservation::factory()->create([
            'vehicle_id' => $vehicle->id,
            'confirmed_at' => Carbon::now(),
        ]);
        $reservation->recordProcessing('crm-export', ProcessingLog::STATUS_SUCCESS, 'dealer-web', 'Exported');

        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->getJson(route('tenant.reservations.show', ['reservation' => $reservation->id]));

        $response->assertStatus(200);
        $this->assertEquals('crm-export', $response->json('processing_logs.0.action'));
        $this->assertEquals('dealer-web', $response->json('processing_logs.0.channel'));
        $this->assertEquals(ProcessingLog::STATUS_SUCCESS, $response->json('processing_logs.0.status'));
    }
}

/**
 * Minimal CRM provider that always fails, for the export-logging test.
 */
class FailingReservationCrmStub implements EnquiryAction, ForwardsReservationToCrm
{
    public function enabled(): bool
    {
        return true;
    }

    public function sendReservation(Reservation $reservation): bool
    {
        throw new \Exception('boom');
    }

    public function formAdditionalDataFields(): array
    {
        return [];
    }

    public function formQuestionDataFields(): array
    {
        return [];
    }

    public function handle(EnquiryActionModel $action, EnquiryModel $enquiry): ?bool
    {
        return null;
    }

    public function successDetails(): string
    {
        return 'ok';
    }

    public function failureReason(): string
    {
        return 'failed';
    }
}

/**
 * Minimal CRM provider that always succeeds, for the export-logging test.
 */
class SucceedingReservationCrmStub implements EnquiryAction, ForwardsReservationToCrm
{
    public function enabled(): bool
    {
        return true;
    }

    public function sendReservation(Reservation $reservation): bool
    {
        return true;
    }

    public function formAdditionalDataFields(): array
    {
        return [];
    }

    public function formQuestionDataFields(): array
    {
        return [];
    }

    public function handle(EnquiryActionModel $action, EnquiryModel $enquiry): ?bool
    {
        return null;
    }

    public function successDetails(): string
    {
        return 'Exported successfully';
    }

    public function failureReason(): string
    {
        return 'nope';
    }
}

/**
 * Minimal CRM provider that would fail if a service booking reached it, for the skip test.
 */
class FailingServiceBookingCrmStub implements EnquiryAction, ForwardsServiceBookingToCrm
{
    public function enabled(): bool
    {
        return true;
    }

    public function sendServiceBooking(Booking $booking): bool
    {
        throw new \Exception('boom');
    }

    public function formAdditionalDataFields(): array
    {
        return [];
    }

    public function formQuestionDataFields(): array
    {
        return [];
    }

    public function handle(EnquiryActionModel $action, EnquiryModel $enquiry): ?bool
    {
        return null;
    }

    public function successDetails(): string
    {
        return 'ok';
    }

    public function failureReason(): string
    {
        return 'failed';
    }
}

/**
 * Minimal CRM provider that always succeeds, for the service-booking export test.
 */
class SucceedingServiceBookingCrmStub implements EnquiryAction, ForwardsServiceBookingToCrm
{
    public function enabled(): bool
    {
        return true;
    }

    public function sendServiceBooking(Booking $booking): bool
    {
        return true;
    }

    public function formAdditionalDataFields(): array
    {
        return [];
    }

    public function formQuestionDataFields(): array
    {
        return [];
    }

    public function handle(EnquiryActionModel $action, EnquiryModel $enquiry): ?bool
    {
        return null;
    }

    public function successDetails(): string
    {
        return 'Exported successfully';
    }

    public function failureReason(): string
    {
        return 'nope';
    }
}
