<?php

namespace Tests\Unit\Admin;

use App\Admin\OrdersHelper;
use Illuminate\Support\Facades\DB;
use Mtc\Shop\Order;
use Tests\TestCase;

/**
 * Regression tests for orders that lose every line-item row but still need to
 * appear in the admin order list.
 *
 * OrdersHelper used to INNER JOIN order_items, so deleting the last line item
 * (e.g. an admin refund-then-delete) left the order invisible from every tab
 * — Refunded, Processing, All — even though order.status was correct. The fix
 * switches to a LEFT JOIN and relaxes the default "hide consultations" filter
 * so a zero-item order (which cannot be a consultation) still passes.
 *
 * Fixtures use raw inserts rather than the model factories: the legacy `order`
 * and `items` tables carry many NOT-NULL columns with no default, and a focused
 * insert keeps the test about visibility behaviour rather than factory wiring.
 */
class OrdersHelperZeroItemsTest extends TestCase
{
    protected function setUp(): void
    {
        parent::setUp();
        DB::beginTransaction();
    }

    protected function tearDown(): void
    {
        DB::rollBack();
        parent::tearDown();
    }

    public function test_refunded_order_with_zero_items_appears_in_refunded_tab(): void
    {
        $orderId = $this->createBareOrder([
            'status' => Order::STATUS_REFUNDED,
            'paid'   => 1,
        ]);

        $found = $this->runQuery(OrdersHelper::ORDER_TYPE_REFUNDED);

        $this->assertContains($orderId, $found, 'Zero-item refunded order should appear in the Refunded tab.');
    }

    public function test_processing_order_with_zero_items_appears_in_all_tab(): void
    {
        $orderId = $this->createBareOrder([
            'status' => Order::STATUS_PROCESSING,
            'paid'   => 1,
        ]);

        $found = $this->runQuery(null);

        $this->assertContains($orderId, $found, 'Zero-item processing order should appear in the unfiltered All view.');
    }

    public function test_processing_order_with_zero_items_appears_in_processing_tab(): void
    {
        $orderId = $this->createBareOrder([
            'status' => Order::STATUS_PROCESSING,
            'paid'   => 1,
        ]);

        $found = $this->runQuery(OrdersHelper::ORDER_TYPE_PROCESSING);

        $this->assertContains($orderId, $found, 'Zero-item processing order should appear in the Processing tab.');
    }

    public function test_order_with_normal_item_still_appears(): void
    {
        $orderId = $this->createBareOrder([
            'status' => Order::STATUS_REFUNDED,
            'paid'   => 1,
        ]);
        $itemId = $this->createNonConsultationItem();
        $this->attachItem($orderId, $itemId);

        $found = $this->runQuery(OrdersHelper::ORDER_TYPE_REFUNDED);

        $this->assertContains($orderId, $found, 'Refunded order with a normal line item must still appear (regression guard).');
    }

    public function test_archived_zero_item_order_remains_hidden(): void
    {
        $orderId = $this->createBareOrder([
            'status'   => Order::STATUS_REFUNDED,
            'paid'     => 1,
            'archived' => 1,
        ]);

        $found = $this->runQuery(OrdersHelper::ORDER_TYPE_REFUNDED);

        $this->assertNotContains($orderId, $found, 'archived=1 is the soft-delete contract; it must keep hiding the order.');
    }

    /**
     * Inserts an `order` row with values for every NOT-NULL column that has no
     * database default. Override any column via $overrides.
     */
    private function createBareOrder(array $overrides): int
    {
        return DB::table('order')->insertGetId(array_merge([
            'paid'               => 1,
            'date'               => now(),
            'delivery_method_id' => 0,
            'delivery_name'      => 'Test Delivery',
            'delivery_cost'      => 1.99,
            'cost_total'         => 1.99,
            'cost_total_exvat'   => 1.66,
            'status'             => Order::STATUS_PROCESSING,
            'member'             => 0,
            'admin_id'           => 0,
            'coupon_code'        => '',
            'type'               => '',
            'ref'                => '',
            'keywords'           => '',
            'failed'             => 0,
            'notes'              => '',
            'order_ref'          => 'TEST-' . uniqid(),
            'ga_ga'              => '',
            'exported'           => now(),
            'checkout'           => '',
            'blacklisted'        => 0,
            'trustpilot_sent'    => now(),
            'flag'               => 0,
            'message_id'         => 0,
            'tracking_no'        => '',
            'shipping_label'     => '',
            'rm_exported'        => now(),
            'doctor_note'        => '',
            'pharmacist_note'    => '',
            'patient_notes'      => '',
            'rm_failed'          => 0,
            'archived'           => 0,
        ], $overrides));
    }

    /**
     * Inserts a minimal `items` row with a non-consultation product_type, which
     * is what the default "hide consultations" filter keys off.
     */
    private function createNonConsultationItem(): int
    {
        return DB::table('items')->insertGetId([
            'form_id'            => 0,
            'epos_code'          => '',
            'name'               => 'Test Item ' . uniqid(),
            'description'        => '',
            'search_name'        => '',
            'search_description' => '',
            'price'              => 100.00,
            'price_exvat'        => 83.33,
            'sale_price'         => 0,
            'sale_price_exvat'   => 0,
            'sort_price'         => 100.00,
            'hidden'             => 0,
            'stock'              => 99999,
            'product_type'       => 'general',
            'times_purchased'    => 0,
            'deleted'            => 0,
            'vat_rate'           => 20,
            'weight'             => '0',
            'vat_deductable'     => 0,
            'google_category'    => '',
            'num_sold'           => 0,
            'custom_field_set_id' => 0,
            'updated_at'         => now(),
            'created_at'         => now(),
            'seo_title'          => '',
            'seo_keywords'       => '',
            'seo_description'    => '',
            'seo_schema_cache'   => '',
        ]);
    }

    private function attachItem(int $orderId, int $itemId): void
    {
        DB::table('order_items')->insert([
            'order_id'         => $orderId,
            'item_id'          => $itemId,
            'assessment_id'    => 0,
            'item_name'        => 'Test Item',
            'item_price'       => 100,
            'item_price_exvat' => 83.33,
            'quantity'         => 1,
            'recommended_quantity' => 0,
            'size'             => 'One Size',
            'PLU'              => '',
            'tosend'           => 0,
            'sent'             => now(),
            'refund_amount'    => '0',
            'discount'         => 0,
            'sizeid'           => 0,
            'sale_item'        => 0,
            'vat_deductable'   => 0,
            'price_paid'       => 100,
            'price_paid_exvat' => 83.33,
            'approved'         => 1,
        ]);
    }

    /**
     * Returns the order ids returned for the given tab type. Uses an empty
     * params array to isolate the test from permission gates (DOCTOR_ORDER_VIEW
     * et al. change the WHERE clauses but are not what we're verifying here).
     */
    private function runQuery(?string $type): array
    {
        return (new OrdersHelper([]))
            ->getOrdersBuilderQuery($type)
            ->pluck('order.id')
            ->map(static fn ($id) => (int) $id)
            ->all();
    }
}
