<?php

namespace Tests\Tenant;

use App\Contracts\ForwardsReservationToCrm;
use App\Facades\Feature;
use App\Facades\Settings;
use App\Listeners\SendReservationToEnquiryIntegrations;
use Carbon\Carbon;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Mail;
use Mtc\Crm\Contracts\EnquiryAction;
use Mtc\Crm\Contracts\EnquiryActionModel;
use Mtc\Crm\Contracts\EnquiryModel;
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 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';
    }
}
