<?php

namespace Tests\Feature;

use App\Http\Middleware\VerifySpaCsrf;
use App\Models\Form;
use App\Models\FormNotification;
use App\Models\FormNotificationTranslation;
use App\Models\FormTranslation;
use App\Models\Franchisee;
use App\Models\TranslateTemplate;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class FranchiseFormGlobalSyncTest extends TestCase
{
    use RefreshDatabase;

    private User $superAdmin;
    private Franchisee $franchise;
    private User $franchiseUser;

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

        config()->set('cache.stores.redis', ['driver' => 'array', 'serialize' => false]);

        $this->withoutMiddleware([VerifySpaCsrf::class]);

        $this->superAdmin = User::factory()->create([
            'role' => 'super_admin',
            'franchisee_id' => null,
        ]);

        $this->franchise = Franchisee::create([
            'name' => 'Franchise A',
            'franchise_domain' => 'franchise-a.example',
            'zoho_franchise_id' => 'ZOHO-A',
            'is_custom_domain' => false,
            'owner_name' => 'Owner A',
            'email' => 'owner-a@example.com',
            'phone' => '1234567890',
            'status' => 'active',
            'is_website_approved' => true,
            'language' => 'hi',
        ]);

        $this->franchiseUser = User::factory()->create([
            'franchisee_id' => $this->franchise->id,
            'role' => 'franchise_admin',
        ]);

        $this->ensurePublishedTranslateTemplate('hi');
    }

    private function ensurePublishedTranslateTemplate(string $languageCode): void
    {
        $code = strtolower($languageCode);
        TranslateTemplate::query()->updateOrCreate(
            ['language_code' => $code],
            [
                'language_name' => strtoupper($code),
                'status' => 'Published',
                'published_at' => now(),
            ]
        );
    }

    /** Case 1: super admin creates an English form. */
    public function test_super_admin_can_create_english_form(): void
    {
        $response = $this->actingAs($this->superAdmin, 'sanctum')
            ->postJson('/admin/forms/add', $this->englishFormPayload());

        $response->assertOk()->assertJsonPath('status', true);

        $this->assertDatabaseCount('forms', 1);
        $form = Form::first();
        $this->assertTrue((bool) $form->is_global);
        $this->assertCount(1, $form->translations()->get());
    }

    /** Case 2 + Case 3: super admin creates Hindi translation, re-saves; no duplicate steps/pages/fields. */
    public function test_super_admin_hindi_translation_is_idempotent(): void
    {
        $form = $this->createGlobalForm();

        $hindiPayload = $this->hindiFormPayload($form);
        $first = $this->actingAs($this->superAdmin, 'sanctum')
            ->postJson("/admin/forms/{$form->id}", $hindiPayload);
        $first->assertOk();

        $second = $this->actingAs($this->superAdmin, 'sanctum')
            ->postJson("/admin/forms/{$form->id}", $hindiPayload);
        $second->assertOk();

        $hindiCount = FormTranslation::where('form_id', $form->id)->where('language', 'hi')->count();
        $this->assertSame(1, $hindiCount, 'Re-saving the same Hindi translation must not duplicate the row.');

        $hindiTranslation = FormTranslation::where('form_id', $form->id)
            ->where('language', 'hi')
            ->firstOrFail();

        $steps = $hindiTranslation->fields['steps'] ?? [];
        $this->assertCount(1, $steps, 'Re-saving must keep exactly one step.');
        $this->assertCount(2, $steps[0]['fields'], 'Re-saving must keep exactly two fields per step.');
        $this->assertSame(['home'], $hindiTranslation->pages);
        $this->assertSame(1, FormNotification::where('form_id', $form->id)->count());
    }

    /** Case 4: franchise sees Hindi content when its preferred language is Hindi. */
    public function test_franchise_sees_hindi_content_for_global_form(): void
    {
        $form = $this->createGlobalForm(true);

        $response = $this->actingAs($this->franchiseUser, 'sanctum')
            ->getJson("/admin/forms/{$form->id}?language=hi");

        $response->assertOk()
            ->assertJsonPath('data.language', 'hi')
            ->assertJsonPath('data.name', 'पंजीकरण फ़ॉर्म');
    }

    /** Case 5: franchise overrides Hindi form, override is saved against franchise rows only. */
    public function test_franchise_override_does_not_touch_global_rows(): void
    {
        $form = $this->createGlobalForm(true);

        $response = $this->actingAs($this->franchiseUser, 'sanctum')
            ->postJson("/admin/forms/{$form->id}", [
                'language' => 'hi',
                'name' => 'फ्रेंचाइज़ फ़ॉर्म',
                'fields' => $this->hindiFields(),
                'pages' => ['home'],
                'status' => 'Published',
                'notifications' => [
                    [
                        'notification_name' => 'Admin',
                        'event_key' => 'form_submission',
                        'from_email' => 'franchise@example.com',
                        'subject' => 'फ्रेंचाइज़ नया सबमिशन',
                        'message' => '<p>फ्रेंचाइज़ संदेश</p>',
                        'status' => 'Published',
                    ],
                ],
            ]);

        $response->assertOk();

        $globalHindi = FormTranslation::where('form_id', $form->id)->where('language', 'hi')->firstOrFail();
        $this->assertSame('पंजीकरण फ़ॉर्म', $globalHindi->name);

        $franchiseForm = Form::where('parent_form_id', $form->id)
            ->where('franchisee_id', $this->franchise->id)
            ->firstOrFail();
        $franchiseHindi = FormTranslation::where('form_id', $franchiseForm->id)
            ->where('language', 'hi')
            ->firstOrFail();
        $this->assertSame('फ्रेंचाइज़ फ़ॉर्म', $franchiseHindi->name);
    }

    /** Case 6: super admin updates Hindi fields/pages → franchise sees update_available. */
    public function test_franchise_sees_update_available_when_global_fields_change(): void
    {
        [$globalForm, $franchiseForm] = $this->createAdoptedFranchiseSetup();

        $globalHindi = FormTranslation::where('form_id', $globalForm->id)->where('language', 'hi')->firstOrFail();
        $globalHindi->forceFill([
            'fields' => $this->hindiFields(extraField: true),
            'version' => $globalHindi->version + 1,
        ])->save();

        $payload = $this->fetchFranchiseFormPayload($franchiseForm);
        $this->assertTrue((bool) $payload['form_update_available']);
        $this->assertTrue((bool) $payload['update_available']);
    }

    /** Case 7: super admin updates form settings (touchGlobalSettingVersion bumps version). */
    public function test_franchise_sees_update_available_when_form_setting_version_bumps(): void
    {
        [$globalForm, $franchiseForm] = $this->createAdoptedFranchiseSetup();

        $globalHindi = FormTranslation::where('form_id', $globalForm->id)->where('language', 'hi')->firstOrFail();
        $globalHindi->forceFill([
            'form_setting' => ['success_message' => 'नया धन्यवाद'],
            'version' => $globalHindi->version + 1,
        ])->save();

        $payload = $this->fetchFranchiseFormPayload($franchiseForm);
        $this->assertTrue((bool) $payload['update_available']);
    }

    /** Case 8: super admin updates Hindi notification subject/message → franchise sees update_available. */
    public function test_franchise_sees_update_available_when_global_notification_changes(): void
    {
        [$globalForm, $franchiseForm] = $this->createAdoptedFranchiseSetup();

        $globalNotification = FormNotification::where('form_id', $globalForm->id)->firstOrFail();
        $globalNotificationTranslation = FormNotificationTranslation::where('form_notification_id', $globalNotification->id)
            ->where('language', 'hi')
            ->firstOrFail();
        $globalNotificationTranslation->forceFill([
            'subject' => 'अद्यतन विषय',
            'message' => '<p>अद्यतन संदेश</p>',
            'version' => $globalNotificationTranslation->version + 1,
        ])->save();

        $payload = $this->fetchFranchiseFormPayload($franchiseForm);
        $this->assertTrue((bool) $payload['notification_update_available']);
        $this->assertTrue((bool) $payload['update_available']);
    }

    /** Case 9: franchise clicks Accept Latest Update — franchise content matches global Hindi. */
    public function test_accept_latest_update_replaces_franchise_with_global(): void
    {
        [$globalForm, $franchiseForm] = $this->createAdoptedFranchiseSetup();

        $globalHindi = FormTranslation::where('form_id', $globalForm->id)->where('language', 'hi')->firstOrFail();
        $globalHindi->forceFill([
            'name' => 'नया हिंदी नाम',
            'version' => $globalHindi->version + 1,
        ])->save();

        $franchiseHindi = FormTranslation::where('form_id', $franchiseForm->id)->where('language', 'hi')->firstOrFail();
        $franchiseHindi->forceFill([
            'name' => 'पुराना फ्रेंचाइज़ नाम',
            'last_local_edit_at' => now(),
        ])->save();

        $response = $this->actingAs($this->franchiseUser, 'sanctum')
            ->postJson("/api/franchise/forms/{$franchiseForm->id}/accept-global-update", [
                'language' => 'hi',
            ]);

        $response->assertOk();

        $franchiseHindi->refresh();
        $this->assertSame('नया हिंदी नाम', $franchiseHindi->name);
        $this->assertTrue((bool) $franchiseHindi->adopted_from_global);
        $this->assertSame((int) $globalHindi->version, (int) $franchiseHindi->source_global_version);
        $this->assertNull($franchiseHindi->last_local_edit_at);
    }

    /** Case 10: franchise clicks Reset to Global — content is reset to latest global Hindi. */
    public function test_reset_to_global_replaces_franchise_with_global(): void
    {
        [$globalForm, $franchiseForm] = $this->createAdoptedFranchiseSetup();

        $franchiseHindi = FormTranslation::where('form_id', $franchiseForm->id)->where('language', 'hi')->firstOrFail();
        $franchiseHindi->forceFill([
            'name' => 'फ्रेंचाइज़ अनुकूलित',
            'last_local_edit_at' => now(),
        ])->save();

        $response = $this->actingAs($this->franchiseUser, 'sanctum')
            ->postJson("/api/franchise/forms/{$franchiseForm->id}/reset-to-global", [
                'language' => 'hi',
            ]);

        $response->assertOk();

        $globalHindi = FormTranslation::where('form_id', $globalForm->id)->where('language', 'hi')->firstOrFail();
        $franchiseHindi->refresh();
        $this->assertSame($globalHindi->name, $franchiseHindi->name);
        $this->assertTrue((bool) $franchiseHindi->adopted_from_global);
        $this->assertNull($franchiseHindi->last_local_edit_at);
    }

    /** Case 11: accepting/resetting many times never duplicates fields, pages, steps, notifications, or notification translations. */
    public function test_repeated_sync_does_not_duplicate_rows(): void
    {
        [$globalForm, $franchiseForm] = $this->createAdoptedFranchiseSetup();

        for ($i = 0; $i < 4; $i++) {
            $this->actingAs($this->franchiseUser, 'sanctum')
                ->postJson("/api/franchise/forms/{$franchiseForm->id}/accept-global-update", ['language' => 'hi'])
                ->assertOk();

            $this->actingAs($this->franchiseUser, 'sanctum')
                ->postJson("/api/franchise/forms/{$franchiseForm->id}/reset-to-global", ['language' => 'hi'])
                ->assertOk();
        }

        $franchiseHindi = FormTranslation::where('form_id', $franchiseForm->id)->where('language', 'hi')->firstOrFail();
        $steps = $franchiseHindi->fields['steps'] ?? [];

        $this->assertSame(1, FormTranslation::where('form_id', $franchiseForm->id)->where('language', 'hi')->count());
        $this->assertCount(1, $steps);
        $this->assertCount(2, $steps[0]['fields']);
        $this->assertSame(['home'], $franchiseHindi->pages);

        $franchiseNotifications = FormNotification::where('form_id', $franchiseForm->id)->get();
        $this->assertCount(1, $franchiseNotifications);
        $this->assertSame(
            1,
            FormNotificationTranslation::where('form_notification_id', $franchiseNotifications->first()->id)
                ->where('language', 'hi')
                ->count()
        );
    }

    private function createGlobalForm(bool $withHindi = false): Form
    {
        $this->actingAs($this->superAdmin, 'sanctum')
            ->postJson('/admin/forms/add', $this->englishFormPayload())
            ->assertOk();

        $form = Form::firstOrFail();

        if ($withHindi) {
            $this->actingAs($this->superAdmin, 'sanctum')
                ->postJson("/admin/forms/{$form->id}", $this->hindiFormPayload($form))
                ->assertOk();
        }

        return $form->fresh();
    }

    /**
     * @return array{0: Form, 1: Form}
     */
    private function createAdoptedFranchiseSetup(): array
    {
        $globalForm = $this->createGlobalForm(true);

        $this->actingAs($this->franchiseUser, 'sanctum')
            ->postJson("/admin/forms/{$globalForm->id}/adopt-global", ['language' => 'hi'])
            ->assertOk();

        $franchiseForm = Form::where('parent_form_id', $globalForm->id)
            ->where('franchisee_id', $this->franchise->id)
            ->firstOrFail();

        return [$globalForm, $franchiseForm];
    }

    private function fetchFranchiseFormPayload(Form $franchiseForm): array
    {
        $response = $this->actingAs($this->franchiseUser, 'sanctum')
            ->getJson("/admin/forms/{$franchiseForm->id}?language=hi");
        $response->assertOk();

        return $response->json('data');
    }

    private function englishFormPayload(): array
    {
        return [
            'language' => 'en',
            'name' => 'Registration Form',
            'description' => 'Sign up form',
            'status' => 'Published',
            'pages' => ['home'],
            'form_setting' => ['success_message' => 'Thanks!'],
            'fields' => $this->englishFields(),
            'notifications' => [
                [
                    'notification_name' => 'Admin',
                    'event_key' => 'form_submission',
                    'from_email' => 'no-reply@example.com',
                    'subject' => 'New submission',
                    'message' => '<p>Hello {all_fields}</p>',
                    'status' => 'Published',
                ],
            ],
        ];
    }

    private function hindiFormPayload(Form $form): array
    {
        $globalNotification = FormNotification::where('form_id', $form->id)->first();

        return [
            'language' => 'hi',
            'name' => 'पंजीकरण फ़ॉर्म',
            'description' => 'साइन अप फ़ॉर्म',
            'status' => 'Published',
            'pages' => ['home'],
            'form_setting' => ['success_message' => 'धन्यवाद!'],
            'fields' => $this->hindiFields(),
            'notifications' => [
                [
                    'id' => $globalNotification?->id,
                    'notification_name' => $globalNotification?->notification_name ?? 'Admin',
                    'event_key' => 'form_submission',
                    'from_email' => $globalNotification?->from_email ?? 'no-reply@example.com',
                    'subject' => 'नया सबमिशन',
                    'message' => '<p>नमस्ते {all_fields}</p>',
                    'status' => 'Published',
                ],
            ],
        ];
    }

    private function englishFields(): array
    {
        return [
            'steps' => [
                [
                    'id' => 'step-1',
                    'heading' => 'Step 1',
                    'subheading' => '',
                    'order' => 1,
                    'fields' => [
                        ['id' => 'name', 'type' => 'text', 'field' => 'Name', 'placeholder' => '', 'required' => true, 'options' => []],
                        ['id' => 'email', 'type' => 'email', 'field' => 'Email', 'placeholder' => '', 'required' => true, 'options' => []],
                    ],
                ],
            ],
        ];
    }

    private function hindiFields(bool $extraField = false): array
    {
        $fields = [
            ['id' => 'name', 'type' => 'text', 'field' => 'नाम', 'placeholder' => '', 'required' => true, 'options' => []],
            ['id' => 'email', 'type' => 'email', 'field' => 'ईमेल', 'placeholder' => '', 'required' => true, 'options' => []],
        ];

        if ($extraField) {
            $fields[] = ['id' => 'phone', 'type' => 'text', 'field' => 'फोन', 'placeholder' => '', 'required' => false, 'options' => []];
        }

        return [
            'steps' => [
                [
                    'id' => 'step-1',
                    'heading' => 'चरण 1',
                    'subheading' => '',
                    'order' => 1,
                    'fields' => $fields,
                ],
            ],
        ];
    }
}
