<?php

declare(strict_types=1);

use App\Jobs\SyncSupplierProjectJob;
use App\Models\Project;
use App\Models\Tenant;
use App\Services\Supplier\Channel\SupplierProjectChannel;
use Carbon\Carbon;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\DB;
use Tests\Concerns\SharesSupplierPdo;

uses(DatabaseTransactions::class, SharesSupplierPdo::class);

/**
 * Task 4.1 — онлайн-режим в окне 18:00→00:00 МСК откладывает отправку поставщику.
 *
 * Поставщик фиксирует заказ слепком в 21:00. Если онлайн-правка уйдёт ему в 19:00,
 * она перезапишет уже зафиксированное состояние (наш слепок снят в 18:02). Поэтому
 * в окне правка кладётся в supplier_deferred_sync, а FlushDeferredOnlineSyncJob (00:05)
 * досылает её вне окна. Вне окна онлайн работает как раньше (немедленно).
 */
beforeEach(function (): void {
    DB::table('system_settings')->updateOrInsert(
        ['key' => 'supplier_export_mode'],
        ['value' => 'online'],
    );
});

afterEach(fn () => Carbon::setTestNow());

it('в окне 18:00→00:00 онлайн не шлёт поставщику, а откладывает', function (): void {
    Carbon::setTestNow(Carbon::parse('2026-06-25 19:00:00', 'Europe/Moscow'));

    $tenant = Tenant::factory()->create();
    $project = Project::factory()->for($tenant)->create([
        'signal_type' => 'site',
        'signal_identifier' => 'okna.ru',
        'is_active' => true,
        'daily_limit_target' => 10,
    ]);

    // Канал НЕ должен дёргаться — defer происходит до любых обращений к поставщику.
    $channel = Mockery::mock(SupplierProjectChannel::class);
    $channel->shouldNotReceive('createProject');
    $channel->shouldNotReceive('updateProject');
    app()->instance(SupplierProjectChannel::class, $channel);

    (new SyncSupplierProjectJob($project->id))->handle(app(SupplierProjectChannel::class));

    expect(DB::table('supplier_deferred_sync')->where('project_id', $project->id)->exists())->toBeTrue();
});

it('повторная правка в окне не плодит дублей (project_id PK, ON CONFLICT DO NOTHING)', function (): void {
    Carbon::setTestNow(Carbon::parse('2026-06-25 19:30:00', 'Europe/Moscow'));

    $tenant = Tenant::factory()->create();
    $project = Project::factory()->for($tenant)->create([
        'signal_type' => 'site', 'signal_identifier' => 'okna.ru', 'is_active' => true, 'daily_limit_target' => 10,
    ]);

    $channel = Mockery::mock(SupplierProjectChannel::class);
    app()->instance(SupplierProjectChannel::class, $channel);

    (new SyncSupplierProjectJob($project->id))->handle(app(SupplierProjectChannel::class));
    (new SyncSupplierProjectJob($project->id))->handle(app(SupplierProjectChannel::class));

    expect(DB::table('supplier_deferred_sync')->where('project_id', $project->id)->count())->toBe(1);
});

it('вне окна (до 18:00) онлайн НЕ откладывает — идёт обычным путём', function (): void {
    Carbon::setTestNow(Carbon::parse('2026-06-25 12:00:00', 'Europe/Moscow'));

    $tenant = Tenant::factory()->create();
    $project = Project::factory()->for($tenant)->create([
        'signal_type' => 'site', 'signal_identifier' => 'okna.ru', 'is_active' => true, 'daily_limit_target' => 10,
    ]);

    // Вне окна defer-ветка не срабатывает: канал ПОЛУЧИТ обращения (online sync идёт).
    // Не мокаем портал целиком — достаточно проверить, что в defer-очередь НЕ попало.
    $channel = Mockery::mock(SupplierProjectChannel::class);
    $channel->shouldReceive('updateProject')->andReturnNull();
    $channel->shouldReceive('createProject')->andReturn(1);
    app()->instance(SupplierProjectChannel::class, $channel);

    try {
        (new SyncSupplierProjectJob($project->id))->handle(app(SupplierProjectChannel::class));
    } catch (\Throwable) {
        // Онлайн-путь может бросить retry при неполном портал-ответе — нам важна только defer-очередь.
    }

    expect(DB::table('supplier_deferred_sync')->where('project_id', $project->id)->exists())->toBeFalse();
});
