<?php

namespace App\Domains\Purchase\Services;

use App\Domains\Purchase\Models\Purchase;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use InvalidArgumentException;

class TenantTransactionResetService
{
    /** @return array<string, int> */
    public function preview(string $tenantCode): array
    {
        $this->ensureTenantExists($tenantCode);
        $scope = $this->scope($tenantCode);

        return [
            'users_preserved' => DB::table('users')->where('tenant_codigo', $tenantCode)->count(),
            'purchases' => $scope['purchase_ids']->count(),
            'purchase_items' => DB::table('compra_items')->whereIn('compra_id', $scope['purchase_ids'])->count(),
            'telepagos_payments' => DB::table('telepagos_payments')->whereIn('compra_id', $scope['purchase_ids'])->count(),
            'telepagos_qr' => DB::table('telepagos_qr')->whereIn('compra_id', $scope['purchase_ids'])->count(),
            'carts' => $scope['cart_ids']->count(),
            'cart_items' => $scope['cart_item_ids']->count(),
            'tickets' => DB::table('tickets')->where('tenant_code', $tenantCode)->count(),
            'stock_reservations' => $this->reservationQuery($scope)->count(),
            'purchase_changes' => DB::table('value_changes')
                ->where('tenant_code', $tenantCode)
                ->where('trackable_type', Purchase::class)
                ->count(),
            'inventories' => $scope['inventory_ids']->count(),
        ];
    }

    /** @return array<string, int> */
    public function reset(string $tenantCode): array
    {
        $this->ensureTenantExists($tenantCode);

        return DB::transaction(function () use ($tenantCode): array {
            $scope = $this->scope($tenantCode);
            $purchaseItems = DB::table('compra_items')->whereIn('compra_id', $scope['purchase_ids'])->count();
            $cartItems = $scope['cart_item_ids']->count();
            $telepagosPayments = DB::table('telepagos_payments')->whereIn('compra_id', $scope['purchase_ids'])->count();
            $telepagosQr = DB::table('telepagos_qr')->whereIn('compra_id', $scope['purchase_ids'])->count();
            $summary = [
                'stock_reservations_deleted' => $this->reservationQuery($scope)->delete(),
                'tickets_deleted' => DB::table('tickets')->where('tenant_code', $tenantCode)->delete(),
                'purchase_changes_deleted' => DB::table('value_changes')
                    ->where('tenant_code', $tenantCode)
                    ->where('trackable_type', Purchase::class)
                    ->delete(),
                'purchases_deleted' => DB::table('compras')->whereIn('id', $scope['purchase_ids'])->delete(),
                'purchase_items_deleted' => $purchaseItems,
                'telepagos_payments_deleted' => $telepagosPayments,
                'telepagos_qr_deleted' => $telepagosQr,
                'carts_deleted' => DB::table('carritos')->whereIn('id', $scope['cart_ids'])->delete(),
                'cart_items_deleted' => $cartItems,
                'inventories_reset' => $scope['inventory_ids']->count(),
                'users_preserved' => DB::table('users')->where('tenant_codigo', $tenantCode)->count(),
            ];

            DB::table('inventories')
                ->whereIn('id', $scope['inventory_ids'])
                ->update([
                    'real_stock' => DB::raw('real_stock + sold_units'),
                    'reserved_stock' => 0,
                    'sold_units' => 0,
                ]);

            return $summary;
        });
    }

    private function ensureTenantExists(string $tenantCode): void
    {
        if (! DB::table('tenants')->where('codigo', $tenantCode)->exists()) {
            throw new InvalidArgumentException("El tenant {$tenantCode} no existe.");
        }
    }

    /**
     * @return array{
     *     purchase_ids: Collection<int, int>,
     *     cart_ids: Collection<int, int>,
     *     cart_item_ids: Collection<int, int>,
     *     inventory_ids: Collection<int, int>
     * }
     */
    private function scope(string $tenantCode): array
    {
        $catalogItemIds = DB::table('catalog_items')
            ->where('tenant_code', $tenantCode)
            ->pluck('id');
        $purchaseIds = DB::table('compras')
            ->where('tenant_codigo', $tenantCode)
            ->pluck('id');
        $cartIds = DB::table('carritos')
            ->where('tenant_codigo', $tenantCode)
            ->pluck('id');
        $cartItemIds = DB::table('carrito_items')
            ->whereIn('cart_id', $cartIds)
            ->pluck('id');
        $inventoryIds = DB::table('variantes')
            ->whereIn('catalog_item_id', $catalogItemIds)
            ->whereNotNull('inventory_id')
            ->pluck('inventory_id')
            ->merge(
                DB::table('catalog_items')
                    ->whereIn('id', $catalogItemIds)
                    ->whereNotNull('inventory_id')
                    ->pluck('inventory_id'),
            )
            ->map(fn ($id): int => (int) $id)
            ->unique()
            ->values();

        return [
            'purchase_ids' => $purchaseIds->map(fn ($id): int => (int) $id),
            'cart_ids' => $cartIds->map(fn ($id): int => (int) $id),
            'cart_item_ids' => $cartItemIds->map(fn ($id): int => (int) $id),
            'inventory_ids' => $inventoryIds,
        ];
    }

    /**
     * @param array{
     *     purchase_ids: Collection<int, int>,
     *     cart_ids: Collection<int, int>,
     *     cart_item_ids: Collection<int, int>,
     *     inventory_ids: Collection<int, int>
     * } $scope
     */
    private function reservationQuery(array $scope): Builder
    {
        return DB::table('stock_reservations')
            ->where(function (Builder $query) use ($scope): void {
                $query->whereIn('inventory_id', $scope['inventory_ids'])
                    ->orWhereIn('purchase_id', $scope['purchase_ids'])
                    ->orWhereIn('cart_item_id', $scope['cart_item_ids']);
            });
    }
}
