<?php

declare(strict_types=1);

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Maestro\Core\Database\Seeders\ModuleAndNavigationSeeder;
use Maestro\Core\Database\Seeders\UserSeeder;
use Maestro\Core\Modules\Customers\Models\Customer;
use Maestro\Core\Modules\HourRegistration\Models\HourProject;
use Maestro\Core\Modules\Offers\Enums\OfferStatus;
use Maestro\Core\Modules\Offers\Models\Offer;
use Tests\Fixtures\AuthTestUser;
use Tests\TestCase;

class OffersFlowTest extends TestCase
{
    use RefreshDatabase;

    private const PNG_1X1_BASE64 = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==';

    public function test_public_get_marks_sent_offer_as_viewed(): void
    {
        $this->seed([UserSeeder::class, ModuleAndNavigationSeeder::class]);

        $user = AuthTestUser::query()->where('email', 'demo@example.com')->first();
        self::assertNotNull($user);

        $payload = [
            'subject' => 'API offer',
            'sections' => [[
                'title' => 'Block',
                'rows' => [[
                    'pricing_type' => 'one_time',
                    'quantity' => 1,
                    'unit_price' => 50,
                    'vat_rate' => 0,
                ]],
            ]],
        ];

        $created = $this->actingAs($user, 'web')->postJson('/api/offers', $payload);
        $created->assertCreated();
        $id = (int) $created->json('data.id');

        $this->actingAs($user, 'web')->postJson("/api/offers/{$id}/send", [
            'channels' => ['link'],
        ])->assertOk();

        /** @phpstan-ignore-next-line */
        $offer = Offer::query()->findOrFail($id);
        self::assertSame(OfferStatus::Sent, $offer->status);
        self::assertNotNull($offer->public_uuid);

        $this->getJson('/api/offers/public/'.$offer->public_uuid)
            ->assertOk();

        $offer->refresh();
        self::assertSame(OfferStatus::Viewed, $offer->status);
        self::assertNotNull($offer->viewed_at);
    }

    public function test_offer_expires_when_valid_until_passed(): void
    {
        $this->seed([UserSeeder::class, ModuleAndNavigationSeeder::class]);

        $user = AuthTestUser::query()->where('email', 'demo@example.com')->first();
        self::assertNotNull($user);

        $created = $this->actingAs($user, 'web')->postJson('/api/offers', [
            'subject' => 'Old',
            'valid_until' => now()->subDays(3)->toDateString(),
            'sections' => [],
        ]);
        $created->assertCreated();
        $id = (int) $created->json('data.id');

        $this->actingAs($user, 'web')->postJson("/api/offers/{$id}/send", [
            'channels' => ['link'],
        ])->assertOk();

        $offer = Offer::query()->findOrFail($id);

        $this->getJson('/api/offers/public/'.$offer->public_uuid)
            ->assertOk();

        $offer->refresh();
        self::assertSame(OfferStatus::Expired, $offer->status);
    }

    public function test_accept_and_decline_public_offer(): void
    {
        $this->seed([UserSeeder::class, ModuleAndNavigationSeeder::class]);

        $user = AuthTestUser::query()->where('email', 'demo@example.com')->first();
        self::assertNotNull($user);

        $created = $this->actingAs($user, 'web')->postJson('/api/offers', [
            'subject' => 'Deal',
            'valid_until' => now()->addWeek()->toDateString(),
            'sections' => [[
                'title' => 'Services',
                'rows' => [[
                    'pricing_type' => 'one_time',
                    'quantity' => 1,
                    'unit_price' => 120,
                    'vat_rate' => 21,
                ]],
            ]],
        ]);
        $id = (int) $created->json('data.id');

        $this->actingAs($user, 'web')->postJson("/api/offers/{$id}/send", ['channels' => ['link']])->assertOk();

        /** @phpstan-ignore-next-line */
        $offer = Offer::query()->findOrFail($id);
        $uuid = (string) $offer->public_uuid;

        $this->postJson("/api/offers/public/{$uuid}/decline", ['reason' => 'No budget'])
            ->assertOk();

        $offer->refresh();
        self::assertSame(OfferStatus::Lost, $offer->status);

        $created2 = $this->actingAs($user, 'web')->postJson('/api/offers', [
            'subject' => 'Deal 2',
            'valid_until' => now()->addWeek()->toDateString(),
            'sections' => [[
                'title' => 'Services',
                'rows' => [[
                    'pricing_type' => 'one_time',
                    'quantity' => 1,
                    'unit_price' => 120,
                    'vat_rate' => 21,
                ]],
            ]],
        ]);
        $id2 = (int) $created2->json('data.id');

        $this->actingAs($user, 'web')->postJson("/api/offers/{$id2}/send", ['channels' => ['link']])->assertOk();
        /** @phpstan-ignore-next-line */
        $offer2 = Offer::query()->findOrFail($id2);

        $this->postJson('/api/offers/public/'.$offer2->public_uuid.'/accept', [
            'signer_name' => 'Jane Buyer',
            'signature_png_base64' => self::PNG_1X1_BASE64,
        ])->assertOk();

        $offer2->refresh();
        self::assertSame(OfferStatus::Won, $offer2->status);
        self::assertSame('Jane Buyer', $offer2->signer_name);
        self::assertNotEmpty($offer2->signature_path);
    }

    public function test_convert_to_project_when_hours_enabled_and_offer_won(): void
    {
        $this->seed([UserSeeder::class, ModuleAndNavigationSeeder::class]);

        $customer = Customer::query()->create([
            'name' => 'Acme BV',
            'is_active' => true,
        ]);

        $user = AuthTestUser::query()->where('email', 'demo@example.com')->first();
        self::assertNotNull($user);

        $created = $this->actingAs($user, 'web')->postJson('/api/offers', [
            'subject' => 'Consulting bundle',
            'customer_id' => $customer->id,
            'valid_until' => now()->addWeek()->toDateString(),
            'sections' => [[
                'title' => 'Work',
                'rows' => [[
                    'pricing_type' => 'hourly',
                    'quantity' => 40,
                    'unit_price' => 90,
                    'vat_rate' => 21,
                ]],
            ]],
        ]);
        $id = (int) $created->json('data.id');

        $this->actingAs($user, 'web')->postJson("/api/offers/{$id}/send", ['channels' => ['link']])->assertOk();

        $offer = Offer::query()->findOrFail($id);

        $this->postJson('/api/offers/public/'.$offer->public_uuid.'/accept', [
            'signer_name' => 'Buyer',
            'signature_png_base64' => self::PNG_1X1_BASE64,
        ])->assertOk();

        $converted = $this->actingAs($user, 'web')->postJson("/api/offers/{$id}/convert-to-project", [
            'name' => 'Consulting rollout',
        ]);
        $converted->assertOk();

        $offer->refresh();
        self::assertNotNull($offer->hour_project_id);

        $exists = HourProject::query()->whereKey((int) $offer->hour_project_id)
            ->where('customer_id', $customer->id)
            ->exists();
        self::assertTrue($exists);
    }
}
