<?php

namespace Tests\Feature;

use App\Models\Contact;
use App\Models\ContactList;
use App\Models\Tag;
use App\Models\User;
use App\Jobs\ProcessAutomationNodeJob;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Http;
use Tests\TestCase;

class AutomationTest extends TestCase
{
    use RefreshDatabase;

    public function test_webhook_redirect()
    {
        $user = User::factory()->create();
        $response = $this->actingAs($user)->get('/webhook');
        $response->assertRedirect('/webhooks');
    }

    public function test_test_webhook_endpoint()
    {
        Http::fake([
            'https://example.com/test' => Http::response(['success' => true], 200)
        ]);

        $user = User::factory()->create();
        $response = $this->actingAs($user)->postJson('/automation/test-webhook', [
            'url' => 'https://example.com/test',
            'method' => 'POST',
            'payload_type' => 'json'
        ]);

        $response->assertStatus(200);
        $response->assertJson([
            'success' => true,
            'status' => 200
        ]);
    }

    public function test_condition_node_execution()
    {
        $user = User::factory()->create();
        $contact = Contact::create([
            'user_id' => $user->id,
            'first_name' => 'Test',
            'last_name' => 'User',
            'email' => 'test@example.com',
            'status' => 'subscribed',
            'contact_type' => 'lead',
            'total_points' => 80,
            'life_time_value' => 150.00
        ]);

        $list = ContactList::create(['user_id' => $user->id, 'title' => 'Test List', 'slug' => 'test-list']);
        $contact->lists()->attach($list);

        $tag = Tag::create(['user_id' => $user->id, 'title' => 'Test Tag', 'slug' => 'test-tag']);
        $contact->tags()->attach($tag);

        $job = new ProcessAutomationNodeJob(1, $contact->id, 'node_id');
        $ref = new \ReflectionClass(ProcessAutomationNodeJob::class);
        $method = $ref->getMethod('executeConditionNode');
        $method->setAccessible(true);

        // 1. Test Subscription Status (subscribed = yes)
        $node = [
            'config' => [
                'conditions' => [
                    ['field' => 'Subscription Status', 'operator' => 'equals', 'value' => 'subscribed']
                ]
            ]
        ];
        $result = $method->invoke($job, $contact, $node);
        $this->assertEquals('yes', $result);

        // 2. Test Subscription Status (unsubscribed = no)
        $node = [
            'config' => [
                'conditions' => [
                    ['field' => 'Subscription Status', 'operator' => 'equals', 'value' => 'unsubscribed']
                ]
            ]
        ];
        $result = $method->invoke($job, $contact, $node);
        $this->assertEquals('no', $result);

        // 3. Test List Membership
        $node = [
            'config' => [
                'conditions' => [
                    ['field' => 'List', 'operator' => 'equals', 'value' => $list->id]
                ]
            ]
        ];
        $result = $method->invoke($job, $contact, $node);
        $this->assertEquals('yes', $result);

        // 4. Test Tag Membership
        $node = [
            'config' => [
                'conditions' => [
                    ['field' => 'Tag', 'operator' => 'equals', 'value' => $tag->id]
                ]
            ]
        ];
        $result = $method->invoke($job, $contact, $node);
        $this->assertEquals('yes', $result);

        // 5. Test First Name (contains)
        $node = [
            'config' => [
                'conditions' => [
                    ['field' => 'First Name', 'operator' => 'contains', 'value' => 'es']
                ]
            ]
        ];
        $result = $method->invoke($job, $contact, $node);
        $this->assertEquals('yes', $result);

        // 6. Test First Name (does not contain)
        $node = [
            'config' => [
                'conditions' => [
                    ['field' => 'First Name', 'operator' => 'does not contain', 'value' => 'abc']
                ]
            ]
        ];
        $result = $method->invoke($job, $contact, $node);
        $this->assertEquals('yes', $result);

        // 7. Test Phone Number (starts with)
        $contact->phone = '+1234567890';
        $contact->save();
        $node = [
            'config' => [
                'conditions' => [
                    ['field' => 'Phone Number', 'operator' => 'starts with', 'value' => '+123']
                ]
            ]
        ];
        $result = $method->invoke($job, $contact, $node);
        $this->assertEquals('yes', $result);

        // 8. Test Contact Rating / Lead Points (greater than)
        $node = [
            'config' => [
                'conditions' => [
                    ['field' => 'Contact Rating', 'operator' => 'greater than', 'value' => 50]
                ]
            ]
        ];
        $result = $method->invoke($job, $contact, $node);
        $this->assertEquals('yes', $result);

        // 9. Test Date Added / created_at (is before)
        $node = [
            'config' => [
                'conditions' => [
                    ['field' => 'Date Added', 'operator' => 'is before', 'value' => now()->addDay()->format('Y-m-d')]
                ]
            ]
        ];
        $result = $method->invoke($job, $contact, $node);
        $this->assertEquals('yes', $result);
    }

    public function test_email_node_custom_recipient_and_preheader()
    {
        \Illuminate\Support\Facades\Mail::fake();

        $user = User::factory()->create();
        $automation = \App\Models\Campaign::create([
            'user_id' => $user->id,
            'title' => 'Test Automation',
            'type' => 'automation',
            'status' => 'active',
            'settings' => [
                'nodes' => [],
                'edges' => []
            ]
        ]);

        $contact = Contact::create([
            'user_id' => $user->id,
            'email' => 'contact@gmail.com',
            'first_name' => 'Jane',
            'last_name' => 'Doe',
            'status' => 'subscribed',
            'contact_type' => 'lead'
        ]);

        $node = [
            'id' => 'node_email',
            'type' => 'email',
            'config' => [
                'subject' => 'Hello {{contact.first_name}}!',
                'pre_header' => 'This is a preheader {{contact.last_name}}',
                'send_to_type' => 'custom',
                'custom_email_address' => 'custom-{{contact.email}}',
                'email_body' => '<p>Welcome</p>',
                'blocks' => []
            ]
        ];

        $renderer = new \App\Services\EmailRenderer();
        $job = new ProcessAutomationNodeJob($automation->id, $contact->id, 'node_email');
        
        $ref = new \ReflectionClass(ProcessAutomationNodeJob::class);
        $method = $ref->getMethod('executeEmailNode');
        $method->setAccessible(true);
        
        $status = $method->invoke($job, $automation, $contact, $node, $renderer);
        
        $this->assertEquals('sent', $status);
        $this->assertEquals(1, $automation->emails_sent);
        
        $this->assertDatabaseHas('campaign_events', [
            'campaign_id' => $automation->id,
            'contact_id' => $contact->id,
            'event_type' => 'sent'
        ]);
    }

    public function test_multiple_triggers_logical_evaluation()
    {
        $user = User::factory()->create();
        
        // Create tags and lists
        $tag = Tag::create(['user_id' => $user->id, 'title' => 'Important', 'slug' => 'important']);
        $list = ContactList::create(['user_id' => $user->id, 'title' => 'Newsletter', 'slug' => 'newsletter']);
        
        $contact = Contact::create([
            'user_id' => $user->id,
            'email' => 'multiple-trigger@example.com',
            'first_name' => 'John',
            'last_name' => 'Doe',
            'status' => 'subscribed',
            'contact_type' => 'lead'
        ]);

        // 1. Test OR triggers (Match ANY)
        $automationOr = \App\Models\Campaign::create([
            'user_id' => $user->id,
            'title' => 'Test OR Automation',
            'type' => 'automation',
            'status' => 'active',
            'settings' => [
                'trigger' => [
                    'logical_operator' => 'OR',
                    'triggers' => [
                        ['type' => 'subscribes_to_list', 'list_id' => $list->id],
                        ['type' => 'tag_applied', 'tag_id' => $tag->id]
                    ]
                ],
                'nodes' => [
                    ['id' => 'n_trigger', 'type' => 'trigger']
                ],
                'edges' => []
            ]
        ]);

        // Attach tag to contact
        $contact->tags()->attach($tag);

        // Simulate activity: Tag Added
        $activityTag = \App\Models\ContactActivity::create([
            'contact_id' => $contact->id,
            'type' => 'tag_added',
            'description' => 'Tag added'
        ]);

        // Verify contact enrolled in OR automation
        $this->assertDatabaseHas('campaign_events', [
            'campaign_id' => $automationOr->id,
            'contact_id' => $contact->id,
            'event_type' => 'enrolled'
        ]);

        // 2. Test AND triggers (Match ALL)
        $automationAnd = \App\Models\Campaign::create([
            'user_id' => $user->id,
            'title' => 'Test AND Automation',
            'type' => 'automation',
            'status' => 'active',
            'settings' => [
                'trigger' => [
                    'logical_operator' => 'AND',
                    'triggers' => [
                        ['type' => 'subscribes_to_list', 'list_id' => $list->id],
                        ['type' => 'tag_applied', 'tag_id' => $tag->id]
                    ]
                ],
                'nodes' => [
                    ['id' => 'n_trigger', 'type' => 'trigger']
                ],
                'edges' => []
            ]
        ]);

        // Clear campaign events for clean testing
        \App\Models\CampaignEvent::where('campaign_id', $automationAnd->id)->delete();

        // Check AND with only tag applied (should NOT enroll since not in list)
        $activityTag2 = \App\Models\ContactActivity::create([
            'contact_id' => $contact->id,
            'type' => 'tag_added',
            'description' => 'Tag added 2'
        ]);

        $this->assertDatabaseMissing('campaign_events', [
            'campaign_id' => $automationAnd->id,
            'contact_id' => $contact->id,
            'event_type' => 'enrolled'
        ]);

        // Now attach list to contact
        $contact->lists()->attach($list);
        
        // Simulate activity: List Added
        $activityList = \App\Models\ContactActivity::create([
            'contact_id' => $contact->id,
            'type' => 'list_added',
            'description' => 'List added'
        ]);

        // Trigger Engine check again (both tag and list are satisfied, and list_added is the event)
        $this->assertDatabaseHas('campaign_events', [
            'campaign_id' => $automationAnd->id,
            'contact_id' => $contact->id,
            'event_type' => 'enrolled'
        ]);
    }

    public function test_custom_flow_condition_edge_fallback()
    {
        Http::fake([
            'https://example.com/webhook' => Http::response(['success' => true], 200)
        ]);

        $user = User::factory()->create();
        
        $nodes = [
            [
                'id' => 'n_trigger',
                'type' => 'trigger',
                'title' => 'Trigger',
                'config' => []
            ],
            [
                'id' => 'n_condition',
                'type' => 'condition',
                'title' => 'If / Else Condition',
                'config' => [
                    'conditions' => [
                        ['field' => 'Subscription Status', 'operator' => 'equals', 'value' => 'subscribed']
                    ]
                ]
            ],
            [
                'id' => 'n_webhook',
                'type' => 'webhook',
                'title' => 'Outgoing Webhook',
                'config' => [
                    'method' => 'POST',
                    'url' => 'https://example.com/webhook',
                    'payload_type' => 'json'
                ]
            ]
        ];

        $edges = [
            [
                'source' => 'n_trigger',
                'target' => 'n_condition',
                'sourcePort' => 'bottom',
                'targetPort' => 'top'
            ],
            [
                'source' => 'n_condition',
                'target' => 'n_webhook',
                'sourcePort' => 'bottom',  // No branch key here, simulates custom flow
                'targetPort' => 'top'
            ]
        ];

        $automation = \App\Models\Campaign::create([
            'user_id' => $user->id,
            'title' => 'Custom Flow Test',
            'type' => 'automation',
            'status' => 'active',
            'settings' => [
                'nodes' => $nodes,
                'edges' => $edges
            ]
        ]);

        $contact = Contact::create([
            'user_id' => $user->id,
            'email' => 'custom-flow-fallback@example.com',
            'status' => 'subscribed',
            'contact_type' => 'lead'
        ]);

        // Dispatch the job for the condition node. Under sync queue, it executes synchronously.
        ProcessAutomationNodeJob::dispatch($automation->id, $contact->id, 'n_condition');

        // Assert condition node succeeded
        $this->assertDatabaseHas('campaign_events', [
            'campaign_id' => $automation->id,
            'contact_id' => $contact->id,
            'node_id' => 'n_condition',
            'event_type' => 'node_success',
            'reason' => 'Condition evaluated to YES'
        ]);

        // Assert webhook node was executed successfully
        $this->assertDatabaseHas('campaign_events', [
            'campaign_id' => $automation->id,
            'contact_id' => $contact->id,
            'node_id' => 'n_webhook',
            'event_type' => 'node_success',
            'reason' => 'Webhook request sent.'
        ]);

        // Assert that the contact logged activity about webhook being sent
        $this->assertDatabaseHas('contact_activities', [
            'contact_id' => $contact->id,
            'type' => 'webhook_sent'
        ]);
    }

    public function test_automation_report_rendering()
    {
        $user = User::factory()->create();
        $automation = \App\Models\Campaign::create([
            'user_id' => $user->id,
            'title' => 'Test Automation Funnel',
            'type' => 'automation',
            'status' => 'active',
            'settings' => [
                'nodes' => [
                    ['id' => 'n_trigger', 'type' => 'trigger', 'title' => 'Sign up'],
                    ['id' => 'n_email', 'type' => 'email', 'title' => 'Send Welcome Email']
                ],
                'edges' => [
                    ['source' => 'n_trigger', 'target' => 'n_email']
                ]
            ],
            'created_by' => $user->id
        ]);

        $response = $this->actingAs($user)->get("/automation/{$automation->id}/report");

        $response->assertStatus(200);
        $response->assertInertia(fn ($page) => $page
            ->component('Automation/Report')
            ->has('automation')
            ->has('nodeStats')
            ->has('contacts')
            ->has('events')
        );
    }

    public function test_automation_contact_journey_api()
    {
        $user = User::factory()->create();
        $automation = \App\Models\Campaign::create([
            'user_id' => $user->id,
            'title' => 'Test Journey Funnel',
            'type' => 'automation',
            'status' => 'active',
            'settings' => [
                'nodes' => [
                    ['id' => 'n_trigger', 'type' => 'trigger', 'title' => 'Sign up']
                ],
                'edges' => []
            ],
            'created_by' => $user->id
        ]);

        $contact = Contact::create([
            'user_id' => $user->id,
            'email' => 'journey@example.com',
            'status' => 'subscribed',
            'contact_type' => 'lead'
        ]);

        // Create some events
        \App\Models\CampaignEvent::create([
            'campaign_id' => $automation->id,
            'contact_id' => $contact->id,
            'event_type' => 'enrolled',
            'node_id' => 'n_trigger',
            'reason' => 'Contact enrolled.'
        ]);

        $response = $this->actingAs($user)->getJson("/automation/{$automation->id}/contacts/{$contact->id}/journey");

        $response->assertStatus(200);
        $response->assertJsonStructure([
            'success',
            'contact' => ['id', 'email', 'name'],
            'journey' => [
                '*' => ['id', 'event_type', 'node_type', 'node_title', 'reason', 'created_at']
            ]
        ]);
    }

    public function test_campaign_email_node_processing()
    {
        \Illuminate\Support\Facades\Mail::fake();

        $user = User::factory()->create();
        $automation = \App\Models\Campaign::create([
            'user_id' => $user->id,
            'title' => 'Test Automation',
            'type' => 'automation',
            'status' => 'active',
            'settings' => [
                'nodes' => [
                    ['id' => 'n_campaign_email', 'type' => 'campaign_email', 'title' => 'Campaign Mail Step', 'config' => ['imported_campaign_id' => null]]
                ],
                'edges' => []
            ]
        ]);

        $contact = Contact::create([
            'user_id' => $user->id,
            'email' => 'campaign@gmail.com',
            'first_name' => 'John',
            'status' => 'subscribed',
            'contact_type' => 'lead'
        ]);

        \App\Models\CampaignEvent::create([
            'campaign_id' => $automation->id,
            'contact_id' => $contact->id,
            'event_type' => 'node_processing',
            'node_id' => 'n_campaign_email',
            'reason' => 'Step is currently executing.'
        ]);

        $job = new ProcessAutomationNodeJob($automation->id, $contact->id, 'n_campaign_email');
        $job->handle(new \App\Services\EmailRenderer());

        $this->assertDatabaseHas('campaign_events', [
            'campaign_id' => $automation->id,
            'contact_id' => $contact->id,
            'event_type' => 'node_success',
            'node_id' => 'n_campaign_email'
        ]);
    }

    public function test_incoming_webhook_trigger()
    {
        $user = \App\Models\User::factory()->create();
        $tag = Tag::create(['user_id' => $user->id, 'title' => 'Test Tag', 'slug' => 'test-tag']);

        $automation = \App\Models\Campaign::create([
            'user_id' => $user->id,
            'title' => 'Webhook Trigger Automation',
            'type' => 'automation',
            'status' => 'active',
            'settings' => [
                'nodes' => [
                    ['id' => 'n_incoming', 'type' => 'incoming_webhook', 'title' => 'Incoming Webhook Target'],
                    ['id' => 'n_apply_tag', 'type' => 'apply_tag', 'title' => 'Apply Tag Step', 'config' => ['tag_id' => $tag->id]]
                ],
                'edges' => [
                    ['source' => 'n_incoming', 'target' => 'n_apply_tag']
                ]
            ]
        ]);

        $response = $this->postJson("/api/webhooks/incoming/{$automation->id}/n_incoming", [
            'email' => 'webhook-contact@example.com',
            'first_name' => 'Webhook',
            'last_name' => 'User'
        ]);

        $response->assertStatus(200);
        $response->assertJson([
            'success' => true
        ]);

        $this->assertDatabaseHas('contacts', [
            'user_id' => $user->id,
            'email' => 'webhook-contact@example.com',
            'first_name' => 'Webhook',
            'last_name' => 'User'
        ]);

        $this->assertDatabaseHas('campaign_events', [
            'campaign_id' => $automation->id,
            'event_type' => 'enrolled',
            'node_id' => 'n_incoming'
        ]);
    }

    public function test_incoming_webhook_trigger_block_single()
    {
        $user = \App\Models\User::factory()->create();
        $webhook = \App\Models\Webhook::create([
            'user_id' => $user->id,
            'name' => 'Test Inbound Webhook',
            'is_active' => true,
            'default_status' => 'subscribed',
            'duplicate_handling' => 'update'
        ]);

        $tag = Tag::create(['user_id' => $user->id, 'title' => 'Webhook Tag', 'slug' => 'webhook-tag']);

        $automation = \App\Models\Campaign::create([
            'user_id' => $user->id,
            'title' => 'Trigger Block Webhook Automation',
            'type' => 'automation',
            'status' => 'active',
            'settings' => [
                'trigger' => [
                    'logical_operator' => 'OR',
                    'triggers' => [
                        ['type' => 'incoming_webhook', 'webhook_id' => $webhook->id]
                    ]
                ],
                'nodes' => [
                    ['id' => 'n_trigger', 'type' => 'trigger', 'title' => 'Trigger'],
                    ['id' => 'n_apply_tag', 'type' => 'apply_tag', 'title' => 'Apply Tag', 'config' => ['tag_id' => $tag->id]]
                ],
                'edges' => [
                    ['source' => 'n_trigger', 'target' => 'n_apply_tag']
                ]
            ]
        ]);

        $response = $this->postJson("/api/webhooks/receive/{$webhook->uuid}", [
            'email' => 'inbound-trigger@example.com',
            'first_name' => 'Inbound',
            'last_name' => 'Lead'
        ]);

        $response->assertStatus(200);

        $this->assertDatabaseHas('contacts', [
            'user_id' => $user->id,
            'email' => 'inbound-trigger@example.com'
        ]);

        $contact = \App\Models\Contact::where('email', 'inbound-trigger@example.com')->first();

        $this->assertDatabaseHas('campaign_events', [
            'campaign_id' => $automation->id,
            'contact_id' => $contact->id,
            'event_type' => 'enrolled',
            'node_id' => 'n_trigger'
        ]);
    }

    public function test_incoming_webhook_trigger_block_and_operator()
    {
        $user = \App\Models\User::factory()->create();
        $webhook = \App\Models\Webhook::create([
            'user_id' => $user->id,
            'name' => 'Target Webhook',
            'is_active' => true,
            'default_status' => 'subscribed',
            'duplicate_handling' => 'update'
        ]);

        $list = \App\Models\ContactList::create([
            'user_id' => $user->id,
            'title' => 'VIP List',
            'slug' => 'vip-list'
        ]);

        $tag = Tag::create(['user_id' => $user->id, 'title' => 'Result Tag', 'slug' => 'result-tag']);

        // Automation requires BOTH Incoming Webhook AND Subscribes to VIP List (AND operator)
        $automation = \App\Models\Campaign::create([
            'user_id' => $user->id,
            'title' => 'AND Webhook List Automation',
            'type' => 'automation',
            'status' => 'active',
            'settings' => [
                'trigger' => [
                    'logical_operator' => 'AND',
                    'triggers' => [
                        ['type' => 'incoming_webhook', 'webhook_id' => $webhook->id],
                        ['type' => 'subscribes_to_list', 'list_id' => $list->id]
                    ]
                ],
                'nodes' => [
                    ['id' => 'n_trigger', 'type' => 'trigger', 'title' => 'Trigger'],
                    ['id' => 'n_tag', 'type' => 'apply_tag', 'title' => 'Apply Tag', 'config' => ['tag_id' => $tag->id]]
                ],
                'edges' => [
                    ['source' => 'n_trigger', 'target' => 'n_tag']
                ]
            ]
        ]);

        // Step 1: Fire Webhook for contact NOT in list yet -> should NOT enroll because AND condition fails
        $response1 = $this->postJson("/api/webhooks/receive/{$webhook->uuid}", [
            'email' => 'and-test@example.com',
            'first_name' => 'AND',
            'last_name' => 'User'
        ]);
        $response1->assertStatus(200);

        $contact = \App\Models\Contact::where('email', 'and-test@example.com')->first();
        $this->assertNotNull($contact);

        $this->assertDatabaseMissing('campaign_events', [
            'campaign_id' => $automation->id,
            'contact_id' => $contact->id,
            'event_type' => 'enrolled'
        ]);

        // Step 2: Now attach contact to VIP list -> BOTH Webhook received AND List condition now satisfied -> should enroll!
        $contact->lists()->attach($list->id);
        $contact->logActivity('list_added', "Added to list: {$list->title}");

        $this->assertDatabaseHas('campaign_events', [
            'campaign_id' => $automation->id,
            'contact_id' => $contact->id,
            'event_type' => 'enrolled'
        ]);
    }
}
