<?php

declare(strict_types=1);

namespace Tests\Support\Imitation;

use App\Models\Project;
use App\Models\Tenant;
use Illuminate\Support\Facades\DB;

/**
 * Рычаги условий для имитационного стенда (Phase 1).
 *
 * Напрямую пишет в реальные колонки, которые читает прод-код:
 *   - tenants.balance_rub           — LedgerService (bcmath balance_rub*100 >= price)
 *   - tenants.frozen_by_balance_at  — PreflightBalanceService (NULL = активен)
 *   - projects.is_active            — SnapshotRebuildCommand eligibility
 *   - projects.delivered_today      — LeadRouter остаток лимита
 *   - projects.delivery_days_mask   — LeadRouter / SnapshotRebuildCommand
 *   - projects.regions              — LeadRouter regional cascade
 *   - projects.preflight_blocked_at — SnapshotRebuildCommand eligibility
 *
 * Имена колонок подтверждены чтением db/schema.sql и прод-кода:
 *   - balance_rub: tenants, DECIMAL(12,2) DEFAULT 0
 *   - frozen_by_balance_at: tenants, TIMESTAMPTZ NULL (NULL = не заморожен)
 *   - regions: projects, INT[] NOT NULL DEFAULT '{}'  (порядковые коды 1..89, НЕ ГИБДД)
 *   - delivery_days_mask: projects, INT NOT NULL DEFAULT 127  (bit 0=Пн..bit 6=Вс)
 *   - daily_limit_target: projects, INT NOT NULL DEFAULT 10
 *   - delivered_today: projects, INT (остаток лимита)
 *   - preflight_blocked_at: projects, TIMESTAMPTZ NULL
 *
 * Коды субъектов — ПОРЯДКОВЫЕ 1..89 (конституционный порядок), НЕ коды ГИБДД.
 * Использовать только через App\Support\RussianRegions::CODE_TO_NAME / nameToCode().
 * Например: Москва = 82, Санкт-Петербург = 83.
 *
 * Task 3 — Phase 1 Portal Client Imitation Harness.
 * Spec: docs/superpowers/specs/2026-06-03-portal-client-imitation-phase1-design.md
 */
final class ConditionLevers
{
    /**
     * Установить баланс тенанта (в рублях, как DECIMAL(12,2)).
     *
     * @param  int|float|string  $rub  Сумма в рублях (например 500.00 или 0).
     */
    public static function setBalance(Tenant $tenant, int|float|string $rub): void
    {
        DB::table('tenants')
            ->where('id', $tenant->id)
            ->update(['balance_rub' => $rub]);
    }

    /**
     * Обнулить баланс тенанта до 0 (лид не пройдёт LedgerService::chargeForDelivery).
     */
    public static function drainBalance(Tenant $tenant): void
    {
        self::setBalance($tenant, 0);
    }

    /**
     * Выставить delivered_today = daily_limit_target у проекта,
     * чтобы LeadRouter не считал его eligible (лимит исчерпан).
     *
     * LeadRouter: `projects.delivered_today < snap.daily_limit` — равенство = не eligible.
     */
    public static function fillToLimit(Project $project): void
    {
        $limit = (int) DB::table('projects')
            ->where('id', $project->id)
            ->value('daily_limit_target');

        DB::table('projects')
            ->where('id', $project->id)
            ->update(['delivered_today' => $limit]);
    }

    /**
     * Приостановить проект: is_active = false + paused_at = NOW().
     *
     * SnapshotRebuildCommand исключает проекты с is_active = false из нового snapshot.
     * (Проверено: команда WHERE p.is_active = true.)
     */
    public static function pause(Project $project): void
    {
        DB::table('projects')
            ->where('id', $project->id)
            ->update([
                'is_active' => false,
                'paused_at' => now(),
            ]);
    }

    /**
     * Заморозить тенанта по балансу: frozen_by_balance_at = NOW().
     *
     * LeadRouter: WHERE tenants.frozen_by_balance_at IS NULL — заморожен = не eligible.
     * SnapshotRebuildCommand: WHERE t.frozen_by_balance_at IS NULL — не попадёт в snapshot.
     */
    public static function freeze(Tenant $tenant): void
    {
        DB::table('tenants')
            ->where('id', $tenant->id)
            ->update(['frozen_by_balance_at' => now()]);
    }

    /**
     * Установить регионы проекта (порядковые коды 1..89, НЕ коды ГИБДД).
     *
     * LeadRouter Фаза 1: exact — ?::int = ANY(snap.regions).
     * Пустой массив = «вся РФ» (LeadRouter Фаза 2, regions = '{}').
     *
     * Пример: [82] = только Москва, [82, 83] = Москва + СПб, [] = вся РФ.
     * Коды через App\Support\RussianRegions::CODE_TO_NAME / nameToCode().
     *
     * @param  array<int>  $codes  Порядковые коды субъектов (1..89).
     */
    public static function setRegions(Project $project, array $codes): void
    {
        // PostgreSQL int[] литерал: '{82,83}' или '{}'.
        $pgArray = '{'.implode(',', array_map('intval', $codes)).'}';

        DB::table('projects')
            ->where('id', $project->id)
            ->update(['regions' => $pgArray]);
    }

    /**
     * Установить битмаску дней приёма лидов.
     *
     * Бит 0 (1) = Понедельник, бит 6 (64) = Воскресенье.
     * 127 = все 7 дней; 31 = Пн–Пт; 96 = Сб+Вс.
     * SnapshotRebuildCommand: WHERE (p.delivery_days_mask & weekdayBit) <> 0.
     *
     * @param  int  $mask  Битмаска 0..127.
     */
    public static function setDays(Project $project, int $mask): void
    {
        DB::table('projects')
            ->where('id', $project->id)
            ->update(['delivery_days_mask' => $mask]);
    }
}
