<?php

namespace Tests\Tenant;

use App\Facades\Feature;
use App\Facades\Settings;
use Carbon\Carbon;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Mtc\Crm\Models\Enquiry;
use Mtc\Crm\Models\Form;
use Mtc\Crm\Models\FormQuestion;
use Mtc\Crm\Models\FormSection;
use Mtc\MercuryDataModels\ResourceView;
use Mtc\MercuryDataModels\Vehicle;
use Tests\TenantTestCase;

class EnquiryControllerTest extends TenantTestCase
{
    public function testSubmitEnquiry()
    {
        $form = Form::factory()->create(['initial_status_id' => 1]);
        $first_name = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'type' => 'text',
            'name' => 'First Name',
            'validation' => 'required',
        ]);
        $last_name = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'type' => 'text',
            'name' => 'Last Name',
            'validation' => 'required',
        ]);
        $email = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'type' => 'email',
            'name' => 'Email',
            'validation' => 'required|email',
        ]);
        $phone = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'type' => 'text',
            'name' => 'Phone Number',
            'validation' => 'required',
        ]);

        $message = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'type' => 'textarea',
            'name' => 'Message',
            'validation' => 'required',
        ]);

        $response = $this
            ->postJson(route('enquiries.store'), [
                'id' => $form->id,
                'questions' => [
                    $first_name->id => 'Peter',
                    $last_name->id => 'Parker',
                    $email->id => 'john@example.com',
                    $phone->id => '12331231232',
                    $message->id => 'Loprem ipsum dolor sit amet',

                ]
            ]);
        $response->assertStatus(200);

        $this->assertTrue(Enquiry::query()->where('form_id', $form->id)->exists());
    }

    public function testFileUploadIsStoredUnderTenantPrefixedPath()
    {
        Storage::fake('file-storage');

        $form = Form::factory()->create(['initial_status_id' => 1]);
        $email = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'type' => 'email',
            'name' => 'Email',
            'validation' => 'required|email',
        ]);
        $upload = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'type' => 'file_upload',
            'name' => 'Document',
            'validation' => 'required|file',
        ]);

        $response = $this->post(route('enquiries.store'), [
            'id' => $form->id,
            'questions' => [
                $email->id => 'john@example.com',
                $upload->id => null,
            ],
            'files' => [
                $upload->id => UploadedFile::fake()->create('document.pdf', 10, 'application/pdf'),
            ],
        ]);

        $response->assertStatus(200);

        $enquiry = Enquiry::query()->where('form_id', $form->id)->firstOrFail();
        $answer = collect($enquiry->details)->firstWhere('id', (string) $upload->id)['answer'];
        $storedPath = is_array($answer) ? $answer[0] : $answer;

        $this->assertStringStartsWith(tenant('id') . '/uploads/', $storedPath);
        Storage::disk('file-storage')->assertExists($storedPath);
    }

    public function testSubmitWithReasonEnquiry()
    {
        $form = Form::factory()->create(['initial_status_id' => 1]);
        $first_name = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'type' => 'text',
            'name' => 'First Name',
            'validation' => 'required',
        ]);
        $last_name = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'type' => 'text',
            'name' => 'Last Name',
            'validation' => 'required',
        ]);
        $email = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'type' => 'email',
            'name' => 'Email',
            'validation' => 'required|email',
        ]);
        $phone = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'type' => 'text',
            'name' => 'Phone Number',
            'validation' => 'required',
        ]);
        $page = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'type' => 'page_id',
            'name' => 'Page ID',
        ]);

        $message = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'type' => 'textarea',
            'name' => 'Message',
            'validation' => 'required',
        ]);

        $response = $this
            ->postJson(route('enquiries.store'), [
                'id' => $form->id,
                'questions' => [
                    $page->id => 'abc',
                    $first_name->id => 'Peter',
                    $last_name->id => 'Parker',
                    $email->id => 'john@example.com',
                    $phone->id => '12331231232',
                    $message->id => 'Loprem ipsum dolor sit amet',

                ]
            ]);
        $response->assertStatus(200);

        $this->assertTrue(Enquiry::query()->where('form_id', $form->id)->exists());
    }

    /**
     * A step submission for a file_upload question with allow_multiple posts the files as
     * an array (files[<id>][]). The step request must validate each element (files.<id>.*) rather
     * than the array as a single file, otherwise it returns 422 "The File Upload must be a file."
     */
    public function testStepAllowsMultipleFilesForMultipleFileUploadQuestion()
    {
        Storage::fake('file-storage');

        $form = Form::factory()->create(['initial_status_id' => 1]);
        $section = FormSection::factory()->create(['form_id' => $form->id]);

        $name = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'form_section_id' => $section->id,
            'type' => 'text',
            'name' => 'First Name',
            'validation' => 'required',
        ]);

        $fileUpload = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'form_section_id' => $section->id,
            'type' => 'file_upload',
            'name' => 'File Upload',
            'validation' => 'file',
            'data' => ['allow_multiple' => true],
        ]);

        $response = $this->post(route('enquiries.step'), [
            'id' => $form->id,
            'section_id' => $section->id,
            'questions' => [
                $name->id => 'Peter',
            ],
            'files' => [
                $fileUpload->id => [
                    UploadedFile::fake()->image('one.jpg'),
                    UploadedFile::fake()->image('two.jpg'),
                    UploadedFile::fake()->image('three.jpg'),
                ],
            ],
        ], ['Accept' => 'application/json']);

        $response->assertStatus(201);
        $response->assertJsonStructure(['enquiry_id']);
    }

    /**
     * A full (single request) form submission for a file_upload question with allow_multiple posts
     * the files as an array (files[<id>][]). Each element must be validated (files.<id>.*) rather
     * than the array as a single file, otherwise it returns 422 "The File Upload must be a file."
     */
    public function testFullFormAllowsMultipleFilesForMultipleFileUploadQuestion()
    {
        Storage::fake('file-storage');

        $form = Form::factory()->create(['initial_status_id' => 1]);

        $name = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'type' => 'text',
            'name' => 'First Name',
            'validation' => 'required',
        ]);

        $fileUpload = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'type' => 'file_upload',
            'name' => 'File Upload',
            'validation' => 'file',
            'data' => ['allow_multiple' => true],
        ]);

        $response = $this->post(route('enquiries.store'), [
            'id' => $form->id,
            'questions' => [
                $name->id => 'Peter',
            ],
            'files' => [
                $fileUpload->id => [
                    UploadedFile::fake()->image('one.jpg'),
                    UploadedFile::fake()->image('two.jpg'),
                    UploadedFile::fake()->image('three.jpg'),
                ],
            ],
        ], ['Accept' => 'application/json']);

        $response->assertStatus(200);
        $this->assertTrue(Enquiry::query()->where('form_id', $form->id)->exists());
    }

    /**
     * A failed upload on a multiple file_upload question reports under the wildcard element key
     * (files.<id>.0) and must use the question's friendly name in the message, not the raw key.
     */
    public function testMultipleFileUploadValidationErrorUsesTheQuestionName()
    {
        $form = Form::factory()->create(['initial_status_id' => 1]);
        $section = FormSection::factory()->create(['form_id' => $form->id]);

        $name = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'form_section_id' => $section->id,
            'type' => 'text',
            'name' => 'First Name',
            'validation' => 'required',
        ]);

        $fileUpload = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'form_section_id' => $section->id,
            'type' => 'file_upload',
            'name' => 'Documents',
            'validation' => 'file',
            'data' => ['allow_multiple' => true],
        ]);

        $response = $this->post(route('enquiries.step'), [
            'id' => $form->id,
            'section_id' => $section->id,
            'questions' => [
                $name->id => 'Peter',
            ],
            'files' => [
                // A non-file element fails the file rule on files.<id>.*
                $fileUpload->id => ['not-a-file'],
            ],
        ], ['Accept' => 'application/json']);

        $response->assertStatus(422);
        $response->assertJsonValidationErrors("files.$fileUpload->id.0");
        // The friendly message uses the question name (not the raw files.<id>.0 key).
        $this->assertStringContainsString(
            'Each file uploaded for Documents must be a valid file.',
            $response->json('message')
        );
    }

    public function testMissingFields()
    {
        $form = Form::factory()->create();
        $first_name = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'type' => 'text',
            'name' => 'First Name',
            'validation' => 'required',
        ]);
        $last_name = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'type' => 'text',
            'name' => 'Last Name',
            'validation' => 'required',
        ]);
        $email = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'type' => 'email',
            'name' => 'Email',
            'validation' => 'required|email',
        ]);
        $phone = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'type' => 'text',
            'name' => 'Phone Number',
            'validation' => 'required',
        ]);

        $message = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'type' => 'textarea',
            'name' => 'Message',
        ]);

        $response = $this
            ->postJson(route('enquiries.store'), [
                'id' => $form->id,
                'questions' => [
                    $first_name->id => 'Peter',
                ]
            ]);
        $response->assertStatus(422);
        $this->assertArrayHasKey('questions.' . $last_name->id, $response->json('errors'));
        $this->assertArrayHasKey('questions.' . $email->id, $response->json('errors'));
        $this->assertArrayHasKey('questions.' . $phone->id, $response->json('errors'));
        $this->assertArrayNotHasKey('questions.' . $message->id, $response->json('errors'));
    }

    public function testEmailTypeValidation()
    {
        $form = Form::factory()->create();
        $email = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'type' => 'email',
            'name' => 'Email',
            'validation' => 'required|email',
        ]);
        $response = $this
            ->postJson(route('enquiries.store'), [
                'id' => $form->id,
                'questions' => [
                    $email->id => 'test-me',
                ]
            ]);
        $response->assertStatus(422);
        $this->assertArrayHasKey('questions.' . $email->id, $response->json('errors'));
    }

    public function testSuccessMessageIsReturned()
    {
        $form = Form::factory()->create(['success_message' => 'foo baz bar']);
        $email = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'type' => 'email',
            'name' => 'Email',
            'validation' => 'required|email',
        ]);
        $response = $this
            ->postJson(route('enquiries.store'), [
                'id' => $form->id,
                'questions' => [
                    $email->id => 'test@example.com',
                ]
            ]);
        $response->assertStatus(200);
        $this->assertArrayHasKey('status', $response->json());
        $this->assertArrayHasKey('message', $response->json());
        $this->assertArrayHasKey('enquiry_id', $response->json());
        $this->assertEquals('foo baz bar', $response->json('message'));
        $this->assertEquals('ok', $response->json('status'));
        $this->assertNotNull($response->json('enquiry_id'));
    }

    public function testRedirectUrlIsReturnedWhenSet()
    {
        $form = Form::factory()->create([
            'success_message' => 'thanks',
            'redirect_url' => 'https://example.com/thank-you',
        ]);
        $email = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'type' => 'email',
            'name' => 'Email',
            'validation' => 'required|email',
        ]);
        $response = $this
            ->postJson(route('enquiries.store'), [
                'id' => $form->id,
                'questions' => [
                    $email->id => 'test@example.com',
                ],
            ]);
        $response->assertStatus(200);
        $this->assertEquals('https://example.com/thank-you', $response->json('redirect_url'));
    }

    public function testRedirectUrlIsNullWhenNotSet()
    {
        $form = Form::factory()->create(['success_message' => 'thanks', 'redirect_url' => null]);
        $email = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'type' => 'email',
            'name' => 'Email',
            'validation' => 'required|email',
        ]);
        $response = $this
            ->postJson(route('enquiries.store'), [
                'id' => $form->id,
                'questions' => [
                    $email->id => 'test@example.com',
                ],
            ]);
        $response->assertStatus(200);
        $this->assertNull($response->json('redirect_url'));
    }

    public function testVehicleStatsReturnedWhenFeatureAndSettingEnabled()
    {
        Feature::setEnabled(tenant(), ['enquiry-vehicle-stats']);
        Settings::make([
            'tab' => 'CRM',
            'section' => 'Enquiries',
            'group' => 'Response',
            'name' => 'Show vehicle stats after enquiry submission',
            'config_key' => 'crm-enquiry-vehicle-stats-enabled',
            'type' => 'boolean',
            'value' => true,
        ]);
        Settings::make([
            'tab' => 'CRM',
            'section' => 'Enquiries',
            'group' => 'Response',
            'name' => 'Days to count views',
            'config_key' => 'crm-enquiry-vehicle-stats-days',
            'type' => 'number',
            'value' => 7,
        ]);

        $vehicle = Vehicle::factory()->create(['is_published' => true]);

        ResourceView::create([
            'viewable_type' => 'vehicle',
            'viewable_id' => $vehicle->id,
            'date' => Carbon::now()->subDays(3),
            'hits' => 150,
        ]);

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

        $form = Form::factory()->create(['initial_status_id' => 1]);
        $email = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'type' => 'email',
            'name' => 'Email',
            'validation' => 'required|email',
        ]);
        $vehicleQuestion = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'type' => 'vehicle_id',
            'name' => 'Vehicle',
        ]);

        $response = $this->postJson(route('enquiries.store'), [
            'id' => $form->id,
            'questions' => [
                $email->id => 'test@example.com',
                $vehicleQuestion->id => $vehicle->id,
            ],
        ]);

        $response->assertStatus(200);
        $response->assertJsonStructure([
            'status',
            'message',
            'enquiry_vehicle_stats' => [
                'days',
                'views',
                'total_enquiries',
            ],
        ]);
        $this->assertEquals(7, $response->json('enquiry_vehicle_stats.days'));
        $this->assertEquals(150, $response->json('enquiry_vehicle_stats.views'));
        $this->assertEquals(2, $response->json('enquiry_vehicle_stats.total_enquiries'));
    }

    public function testVehicleStatsNotReturnedWhenFeatureDisabled()
    {
        Settings::make([
            'tab' => 'CRM',
            'section' => 'Enquiries',
            'group' => 'Response',
            'name' => 'Show vehicle stats after enquiry submission',
            'config_key' => 'crm-enquiry-vehicle-stats-enabled',
            'type' => 'boolean',
            'value' => true,
        ]);

        $vehicle = Vehicle::factory()->create(['is_published' => true]);

        $form = Form::factory()->create(['initial_status_id' => 1]);
        $email = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'type' => 'email',
            'name' => 'Email',
            'validation' => 'required|email',
        ]);
        $vehicleQuestion = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'type' => 'vehicle_id',
            'name' => 'Vehicle',
        ]);

        $response = $this->postJson(route('enquiries.store'), [
            'id' => $form->id,
            'questions' => [
                $email->id => 'test@example.com',
                $vehicleQuestion->id => $vehicle->id,
            ],
        ]);

        $response->assertStatus(200);
        $this->assertEmpty($response->json('enquiry_vehicle_stats'));
    }

    public function testVehicleStatsNotReturnedWhenSettingDisabled()
    {
        Feature::setEnabled(tenant(), ['enquiry-vehicle-stats']);
        Settings::make([
            'tab' => 'CRM',
            'section' => 'Enquiries',
            'group' => 'Response',
            'name' => 'Show vehicle stats after enquiry submission',
            'config_key' => 'crm-enquiry-vehicle-stats-enabled',
            'type' => 'boolean',
            'value' => false,
        ]);

        $vehicle = Vehicle::factory()->create(['is_published' => true]);

        $form = Form::factory()->create(['initial_status_id' => 1]);
        $email = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'type' => 'email',
            'name' => 'Email',
            'validation' => 'required|email',
        ]);
        $vehicleQuestion = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'type' => 'vehicle_id',
            'name' => 'Vehicle',
        ]);

        $response = $this->postJson(route('enquiries.store'), [
            'id' => $form->id,
            'questions' => [
                $email->id => 'test@example.com',
                $vehicleQuestion->id => $vehicle->id,
            ],
        ]);

        $response->assertStatus(200);
        $this->assertEmpty($response->json('enquiry_vehicle_stats'));
    }

    public function testVehicleStatsNotReturnedForNonVehicleEnquiry()
    {
        Feature::setEnabled(tenant(), ['enquiry-vehicle-stats']);
        Settings::make([
            'tab' => 'CRM',
            'section' => 'Enquiries',
            'group' => 'Response',
            'name' => 'Show vehicle stats after enquiry submission',
            'config_key' => 'crm-enquiry-vehicle-stats-enabled',
            'type' => 'boolean',
            'value' => true,
        ]);

        $form = Form::factory()->create(['initial_status_id' => 1]);
        $email = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'type' => 'email',
            'name' => 'Email',
            'validation' => 'required|email',
        ]);

        $response = $this->postJson(route('enquiries.store'), [
            'id' => $form->id,
            'questions' => [
                $email->id => 'test@example.com',
            ],
        ]);

        $response->assertStatus(200);
        $this->assertEmpty($response->json('enquiry_vehicle_stats'));
    }

    public function testVehicleStatsViewsNullWhenBelowThreshold()
    {
        Feature::setEnabled(tenant(), ['enquiry-vehicle-stats']);
        Settings::make([
            'tab' => 'CRM',
            'section' => 'Enquiries',
            'group' => 'Response',
            'name' => 'Show vehicle stats after enquiry submission',
            'config_key' => 'crm-enquiry-vehicle-stats-enabled',
            'type' => 'boolean',
            'value' => true,
        ]);
        Settings::make([
            'tab' => 'CRM',
            'section' => 'Enquiries',
            'group' => 'Response',
            'name' => 'Minimum views threshold',
            'config_key' => 'crm-enquiry-vehicle-stats-views-threshold',
            'type' => 'number',
            'value' => 100,
        ]);
        Settings::make([
            'tab' => 'CRM',
            'section' => 'Enquiries',
            'group' => 'Response',
            'name' => 'Days to count views',
            'config_key' => 'crm-enquiry-vehicle-stats-days',
            'type' => 'number',
            'value' => 7,
        ]);

        $vehicle = Vehicle::factory()->create(['is_published' => true]);

        ResourceView::create([
            'viewable_type' => 'vehicle',
            'viewable_id' => $vehicle->id,
            'date' => Carbon::now()->subDays(3),
            'hits' => 50,
        ]);

        $form = Form::factory()->create(['initial_status_id' => 1]);
        $email = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'type' => 'email',
            'name' => 'Email',
            'validation' => 'required|email',
        ]);
        $vehicleQuestion = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'type' => 'vehicle_id',
            'name' => 'Vehicle',
        ]);

        $response = $this->postJson(route('enquiries.store'), [
            'id' => $form->id,
            'questions' => [
                $email->id => 'test@example.com',
                $vehicleQuestion->id => $vehicle->id,
            ],
        ]);

        $response->assertStatus(200);
        $this->assertNotEmpty($response->json('enquiry_vehicle_stats'));
        $this->assertEquals(7, $response->json('enquiry_vehicle_stats.days'));
        $this->assertNull($response->json('enquiry_vehicle_stats.views'));
        $this->assertEquals(1, $response->json('enquiry_vehicle_stats.total_enquiries'));
    }

    public function testVehicleStatsUsesConfiguredDaysSetting()
    {
        Feature::setEnabled(tenant(), ['enquiry-vehicle-stats']);
        Settings::make([
            'tab' => 'CRM',
            'section' => 'Enquiries',
            'group' => 'Response',
            'name' => 'Show vehicle stats after enquiry submission',
            'config_key' => 'crm-enquiry-vehicle-stats-enabled',
            'type' => 'boolean',
            'value' => true,
        ]);
        Settings::make([
            'tab' => 'CRM',
            'section' => 'Enquiries',
            'group' => 'Response',
            'name' => 'Days to count views',
            'config_key' => 'crm-enquiry-vehicle-stats-days',
            'type' => 'number',
            'value' => 14,
        ]);

        $vehicle = Vehicle::factory()->create(['is_published' => true]);

        ResourceView::create([
            'viewable_type' => 'vehicle',
            'viewable_id' => $vehicle->id,
            'date' => Carbon::now()->subDays(10),
            'hits' => 100,
        ]);

        $form = Form::factory()->create(['initial_status_id' => 1]);
        $email = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'type' => 'email',
            'name' => 'Email',
            'validation' => 'required|email',
        ]);
        $vehicleQuestion = FormQuestion::factory()->create([
            'form_id' => $form->id,
            'type' => 'vehicle_id',
            'name' => 'Vehicle',
        ]);

        $response = $this->postJson(route('enquiries.store'), [
            'id' => $form->id,
            'questions' => [
                $email->id => 'test@example.com',
                $vehicleQuestion->id => $vehicle->id,
            ],
        ]);

        $response->assertStatus(200);
        $this->assertEquals(14, $response->json('enquiry_vehicle_stats.days'));
        $this->assertEquals(100, $response->json('enquiry_vehicle_stats.views'));
    }
}
