<?php

namespace Tests\Unit\Services;

use App\Services\CartService;
use App\Services\OcCustomerService;
use App\Services\OrderCreateService;
use App\Services\OrderMetaService;
use App\Services\SettingsService;
use App\Services\TelecartCustomerService;
use Carbon\Carbon;
use Mockery as m;
use Openguru\OpenCartFramework\OpenCart\Decorators\OcRegistryDecorator;
use Openguru\OpenCartFramework\QueryBuilder\Connections\ConnectionInterface;
use Openguru\OpenCartFramework\Telegram\TelegramService;
use Psr\Log\LoggerInterface;
use Tests\TestCase;

class OrderCreateServiceTest extends TestCase
{
    public function testCreateNewOrder(): void
    {
        $telegramUserId = 9999;
        $customOrderFields = [
            'field_1' => 'кирилица',
            'field_2' => 'hello',
        ];

        $tgData = [
            'user' => [
                'id' => $telegramUserId,
            ],
        ];

        $data = [
            'firstname' => 'John',
            'lastname' => 'Doe',
            'email' => 'test@mail.com',
            'telephone' => '+79999999999',
            'comment' => 'Comment',
            'shipping_address_1' => 'Russia, Moscow',
            'shipping_city' => 'Moscow',
            'shipping_zone' => 'Rostov',
            'shipping_postcode' => 'Rostov',
            'payment_method' => 'Cash',
        ];

        $data = array_merge($data, $customOrderFields);

        $data['tgData'] = $tgData;

        $meta = [
            'ip' => '127.0.0.1',
            'user_agent' => 'UnitTests',
        ];

        $storeId = $this->app->getConfigValue('store.oc_store_id');
        $dateAdded = '2026-01-01 00:00:00';
        $dateAddedFormatted = '01.01.2026 00:00';
        Carbon::setTestNow($dateAdded);
        $totalText = '100.5р.';
        $totalNumeric = 100.5;
        $totals = [];
        $currencyId = 100;
        $currencyCode = $this->app->getConfigValue('store.oc_default_currency');
        $currencyValue = 222;
        $orderId = 1111;
        $orderProductId = 223;
        $ocCustomerId = 333;
        $telecartCustomerId = 444;

        $product = [
            'product_id' => 93,
            'name' => 'Product Name',
            'model' => 'Product Model',
            'quantity' => 1,
            'price_numeric' => 100,
            'total_numeric' => 100,
            'reward_numeric' => 88,
        ];
        $products = [$product];

        $connection = m::mock(ConnectionInterface::class);
        $connection->shouldReceive('beginTransaction')->once()->andReturnTrue();
        $connection->shouldReceive('commitTransaction')->once()->andReturnTrue();
        $connection->shouldReceive('rollBackTransaction')->andReturnTrue();
        $connection->shouldReceive('lastInsertId')->once()->andReturn($orderId)->ordered();
        $connection->shouldReceive('lastInsertId')->once()->andReturn($orderProductId)->ordered();

        $connection->shouldReceive('insert')->once()->with(
            db_table('order'),
            m::on(function ($orderData) use ($storeId, $data, $totalNumeric, $meta, $currencyId, $currencyCode, $currencyValue, $ocCustomerId) {
                return $orderData['store_id'] === $storeId
                    && $orderData['store_name'] === $this->app->getConfigValue('app.app_name')
                    && $orderData['firstname'] === $data['firstname']
                    && $orderData['lastname'] === $data['lastname']
                    && $orderData['email'] === $data['email']
                    && $orderData['telephone'] === $data['telephone']
                    && $orderData['payment_method'] === $data['payment_method']
                    && $orderData['comment'] === $data['comment']
                    && $orderData['shipping_address_1'] === $data['shipping_address_1']
                    && $orderData['shipping_city'] === $data['shipping_city']
                    && $orderData['shipping_zone'] === $data['shipping_zone']
                    && $orderData['shipping_postcode'] === $data['shipping_postcode']
                    && $orderData['total'] === $totalNumeric
                    && $orderData['order_status_id'] === $this->app->getConfigValue('orders.order_default_status_id')
                    && $orderData['ip'] === $meta['ip']
                    && $orderData['forwarded_ip'] === $meta['ip']
                    && $orderData['user_agent'] === $meta['user_agent']
                    && $orderData['date_added'] instanceof Carbon
                    && $orderData['date_modified'] instanceof Carbon
                    && $orderData['language_id'] === $this->app->getConfigValue('app.language_id')
                    && $orderData['currency_id'] === $currencyId
                    && $orderData['currency_code'] === $currencyCode
                    && $orderData['currency_value'] === $currencyValue
                    && $orderData['customer_group_id'] === $this->app->getConfigValue('orders.oc_customer_group_id')
                    && $orderData['customer_id'] === $ocCustomerId;
            }),
        )
            ->andReturn(true);

        $connection->shouldReceive('insert')->once()->with(
            db_table('order_product'),
            [
                'order_id' => $orderId,
                'product_id' => $product['product_id'],
                'name' => $product['name'],
                'model' => $product['model'],
                'quantity' => $product['quantity'],
                'price' => $product['price_numeric'],
                'total' => $product['total_numeric'],
                'reward' => $product['reward_numeric'],
            ]
        )->andReturn(true);

        $connection->shouldReceive('insert')->once()->with(
            db_table('order_history'),
            m::on(function ($historyData) use ($orderId) {
                return $historyData['order_id'] === $orderId
                    && $historyData['order_status_id'] === $this->app->getConfigValue('orders.order_default_status_id')
                    && $historyData['notify'] === 0
                    && $historyData['comment'] === 'Заказ оформлен через Telegram Mini App.'
                        . "\n\nДополнительная информация по заказу:"
                        . "\nfield_1: кирилица"
                        . "\nfield_2: hello\n"
                    && $historyData['date_added'] instanceof Carbon;
            }),
        )->andReturnTrue();

        $cartService = m::mock(CartService::class);
        $cartService
            ->shouldReceive('getCart')
            ->once()
            ->andReturn([
                'total' => $totalNumeric,
                'totals' => $totals,
                'products' => $products,
                'total_text' => $totalText,
            ]);

        $cartService->shouldReceive('flush')->once()->andReturnNull();

        $ocCurrencyMock = m::mock();
        $ocCurrencyMock->shouldReceive('getId')->once()->andReturn($currencyId);
        $ocCurrencyMock->shouldReceive('getValue')->once()->andReturn($currencyValue);
        $ocSessionMock = m::mock();
        $ocSessionMock->data = [
            'currency' => $currencyCode,
        ];

        $registryMock = m::mock('Registry');
        $registryMock->shouldReceive('get')->with('currency')->andReturn($ocCurrencyMock);
        $registryMock->shouldReceive('get')->with('session')->andReturn($ocSessionMock);
        $ocRegistryDecorator = new OcRegistryDecorator($registryMock);

        $telegramServiceMock = m::mock(TelegramService::class);
        $loggerMock = m::mock(LoggerInterface::class);

        $telegramCustomerService = m::mock(TelecartCustomerService::class);
        $telegramCustomerService->shouldReceive('saveOrUpdate')->once()
            ->with($tgData['user'])
            ->andReturn(['id' => $telecartCustomerId]);

        $telegramCustomerService->shouldReceive('increaseOrdersCount')->once()
            ->with($telecartCustomerId)
            ->andReturnNull();

        $ocCustomerService = m::mock(OcCustomerService::class);
        $ocCustomerService->shouldReceive('findOrCreateByTelecartCustomerId')->once()
            ->with($telecartCustomerId, m::type('array'))
            ->andReturn(['customer_id' => $ocCustomerId]);

        $orderMetaService = m::mock(OrderMetaService::class);
        $orderMetaService->shouldReceive('insert')
            ->once()
            ->with($orderId, $storeId, $customOrderFields, $telecartCustomerId)
            ->andReturnNull();

        $service = new OrderCreateService(
            $connection,
            $cartService,
            $ocRegistryDecorator,
            $this->app->get(SettingsService::class),
            $telegramServiceMock,
            $loggerMock,
            $telegramCustomerService,
            $ocCustomerService,
            $orderMetaService
        );

        $order = $service->create($data, $meta);

        $this->assertEquals([
            'id' => $orderId,
            'created_at' => $dateAddedFormatted,
            'total' => $totalText,
            'final_total_numeric' => $totalNumeric,
            'currency' => $currencyCode,
            'products' => $products,
        ], $order);
    }
}
