<?php

declare(strict_types=1);

namespace App\Jobs\Supplier;

use App\Models\SupplierProject;
use App\Services\Supplier\SupplierPortalClient;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Throwable;

/**
 * Удаление/пере-синк доноров у поставщика после удаления Лидерра-проекта.
 *
 * Для каждого supplier_project S (донора), к которому был привязан удалённый проект:
 *   - остались другие потребители (project_supplier_links) → донор нужен другим клиентам:
 *     НЕ удаляем у поставщика, пере-синкаем агрегат (SyncSupplierProjectsJob).
 *   - потребителей не осталось → удаляем у поставщика (deleteProject) + локальную запись S.
 *
 * Spec: docs/superpowers/specs/2026-05-21-project-delete-dedup-errors-design.md §Решение 2.
 */
class DeleteSupplierProjectJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public int $tries = 3;

    public int $backoff = 60;

    public const string DB_CONNECTION = 'pgsql_supplier';

    /** @param array<int,int> $supplierProjectIds */
    public function __construct(public array $supplierProjectIds) {}

    public function handle(SupplierPortalClient $client): void
    {
        $needsResync = false;

        foreach ($this->supplierProjectIds as $id) {
            $sp = SupplierProject::on(self::DB_CONNECTION)->find($id);
            if ($sp === null) {
                continue;
            }

            $remaining = DB::connection(self::DB_CONNECTION)
                ->table('project_supplier_links')
                ->where('supplier_project_id', $id)
                ->count();

            if ($remaining > 0) {
                $needsResync = true;

                continue;
            }

            // Task 2.4: пока летит хвост слепка по этому источнику — НЕ удаляем донора.
            // Удалив supplier_project, мы оборвём матч хвостового лида (LeadRouter ищет
            // snapshot по источнику донора). Откладываем: повтор на следующем
            // CleanupInactiveSupplierProjectsJob (02:00), когда хвост уже долетит.
            if ($this->hasActiveSnapshotTail($sp)) {
                Log::info('supplier.delete_donor_deferred_snapshot_tail', [
                    'supplier_project_id' => $id,
                    'signal_type' => $sp->signal_type,
                    'unique_key' => $sp->unique_key,
                ]);

                continue;
            }

            if ($sp->supplier_external_id !== null && $sp->supplier_external_id !== '') {
                try {
                    $client->deleteProject((int) $sp->supplier_external_id);
                } catch (Throwable $e) {
                    Log::warning('supplier.delete_donor_failed', [
                        'supplier_project_id' => $id, 'error' => $e->getMessage(),
                    ]);
                    throw $e; // retry the job
                }
            }

            $sp->delete();
        }

        if ($needsResync) {
            SyncSupplierProjectsJob::dispatch();
        }
    }

    /**
     * Есть ли снимок маршрутизации за активную дату (сегодня/завтра МСК), чей источник
     * матчится на этого донора. Зеркалит предикат LeadRouter::queryCandidates (source-ветка):
     *   sms  — (sms_senders->>0) || ('+'||keyword) = unique_key;
     *   site — lower(signal_identifier) = lower(unique_key) ИЛИ поддомен '%.'||unique_key;
     *   call — lower(signal_identifier) = lower(unique_key).
     *
     * Если матч есть — хвостовой лид по этому источнику ещё может прийти, донор нужен.
     */
    private function hasActiveSnapshotTail(SupplierProject $sp): bool
    {
        $today = Carbon::today('Europe/Moscow')->toDateString();
        $tomorrow = Carbon::tomorrow('Europe/Moscow')->toDateString();

        $query = DB::connection(self::DB_CONNECTION)
            ->table('project_routing_snapshots')
            ->whereIn('snapshot_date', [$today, $tomorrow]);

        if ($sp->signal_type === 'sms') {
            $query->whereRaw(
                "signal_type = 'sms' AND (
                    (sms_senders ->> 0)
                    || CASE WHEN COALESCE(sms_keyword, '') <> '' THEN '+' || sms_keyword ELSE '' END
                ) = ?",
                [(string) $sp->unique_key],
            );
        } elseif ($sp->signal_type === 'site') {
            $query->whereRaw(
                "signal_type = 'site' AND (
                    LOWER(signal_identifier) = LOWER(?)
                    OR LOWER(signal_identifier) LIKE '%.' || LOWER(?)
                )",
                [(string) $sp->unique_key, (string) $sp->unique_key],
            );
        } else {
            $query->whereRaw(
                'signal_type = ? AND LOWER(signal_identifier) = LOWER(?)',
                [(string) $sp->signal_type, (string) $sp->unique_key],
            );
        }

        return $query->exists();
    }
}
