<?php

namespace Tests\Feature;

use App\Models\Product;
use App\Models\ReconProductMapping;
use App\Models\ReconProductMappingComponent;
use App\Models\Sale;
use App\Models\User;
use App\Models\Warehouse;
use App\Models\WarehouseStock;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Spatie\Permission\Models\Role;
use Tests\TestCase;

class ReconReportTest extends TestCase
{
    use RefreshDatabase;

    private User $manager;
    private Warehouse $warehouse;

    protected function setUp(): void
    {
        parent::setUp();
        Role::firstOrCreate(['name' => 'manager', 'guard_name' => 'web']);
        $this->manager = User::factory()->create();
        $this->manager->assignRole('manager');
        $this->warehouse = Warehouse::create(['code' => 'WH1', 'name' => 'Gudang Utama']);
    }

    private function makeProduct(string $sku, string $name): Product
    {
        $product = Product::create(['sku' => $sku, 'name' => $name, 'sell_price' => 30000]);
        WarehouseStock::create([
            'warehouse_id'  => $this->warehouse->id,
            'product_id'    => $product->id,
            'current_stock' => 1000,
            'min_stock'     => 5,
        ]);

        return $product;
    }

    private function createMapping(string $channel, string $externalKey, Product $product): ReconProductMapping
    {
        $mapping = ReconProductMapping::create([
            'channel'      => $channel,
            'external_key' => $externalKey,
        ]);
        ReconProductMappingComponent::create([
            'mapping_id'     => $mapping->id,
            'product_id'     => $product->id,
            'qty_multiplier' => 1,
        ]);

        return $mapping;
    }

    private function makeSale(string $orderNo, string $channel, string $date, Product $product, int $qty, int $price): Sale
    {
        $this->actingAs($this->manager, 'sanctum')->postJson('/api/sales', [
            'order_no'     => $orderNo,
            'channel'      => $channel,
            'created_at'   => $date,
            'warehouse_id' => $this->warehouse->id,
            'items'        => [
                ['product_id' => $product->id, 'qty' => $qty, 'price' => $price],
            ],
        ])->assertStatus(201);

        return Sale::where('order_no', $orderNo)->first();
    }

    private function importBatch(string $channel, array $rows, bool $supersede = false): int
    {
        $response = $this->actingAs($this->manager, 'sanctum')->postJson('/api/recon/batches', [
            'channel'   => $channel,
            'supersede' => $supersede,
            'rows'      => $rows,
        ]);
        $response->assertStatus(201);

        return $response->json('batch_id');
    }

    private function report(string $channel, string $from, string $to): array
    {
        $response = $this->actingAs($this->manager, 'sanctum')
            ->getJson("/api/recon/report?channel={$channel}&date_from={$from}&date_to={$to}");
        $response->assertStatus(200);

        return $response->json();
    }

    private function rowFor(array $report, string $date, ?int $productId): ?array
    {
        return collect($report['data'])->first(fn($r) =>
            $r['sale_date'] === $date && $r['product_id'] === $productId);
    }

    public function test_report_produces_all_status_types(): void
    {
        $coklat = $this->makeProduct('ESK-COK', 'Es Krim Coklat');
        $vanila = $this->makeProduct('ESK-VAN', 'Es Krim Vanila');
        $stroberi = $this->makeProduct('ESK-STR', 'Es Krim Stroberi');

        foreach ([$coklat, $vanila, $stroberi] as $i => $p) {
            $this->createMapping('shopee', ReconProductMapping::normalizeKey($p->name), $p);
        }

        // match: 3 x 30000 both sides
        $this->makeSale('ORD-1', 'shopee', '2026-07-01', $coklat, 3, 30000);
        // qty_mismatch: manual 2, import 3 (same total amount side note: amounts equal)
        $this->makeSale('ORD-2', 'shopee', '2026-07-01', $vanila, 2, 45000);
        // amount_mismatch: same qty, different amount
        $this->makeSale('ORD-3', 'shopee', '2026-07-02', $coklat, 1, 25000);
        // missing_import: manual sale on covered date, absent from import
        $this->makeSale('ORD-4', 'shopee', '2026-07-02', $stroberi, 5, 30000);

        $this->importBatch('shopee', [
            ['row_number' => 2, 'sale_date' => '2026-07-01', 'external_label' => 'Es Krim Coklat', 'qty' => 3, 'amount' => 90000],
            ['row_number' => 3, 'sale_date' => '2026-07-01', 'external_label' => 'Es Krim Vanila', 'qty' => 3, 'amount' => 90000],
            ['row_number' => 4, 'sale_date' => '2026-07-02', 'external_label' => 'Es Krim Coklat', 'qty' => 1, 'amount' => 30000],
            // missing_manual: imported but never entered manually
            ['row_number' => 5, 'sale_date' => '2026-07-02', 'external_label' => 'Es Krim Vanila', 'qty' => 2, 'amount' => 60000],
            // unmapped: unknown product
            ['row_number' => 6, 'sale_date' => '2026-07-02', 'external_label' => 'Produk Misterius', 'qty' => 1, 'amount' => 15000],
        ]);

        $report = $this->report('shopee', '2026-07-01', '2026-07-05');

        $this->assertSame('match', $this->rowFor($report, '2026-07-01', $coklat->id)['status']);
        $this->assertSame('qty_mismatch', $this->rowFor($report, '2026-07-01', $vanila->id)['status']);
        $this->assertSame('amount_mismatch', $this->rowFor($report, '2026-07-02', $coklat->id)['status']);
        $this->assertSame('missing_import', $this->rowFor($report, '2026-07-02', $stroberi->id)['status']);
        $this->assertSame('missing_manual', $this->rowFor($report, '2026-07-02', $vanila->id)['status']);

        $unmapped = collect($report['data'])->firstWhere('status', 'unmapped');
        $this->assertNotNull($unmapped);
        $this->assertSame('Produk Misterius', $unmapped['external_label']);

        $this->assertSame(1, $report['summary']['by_status']['match']);
    }

    public function test_manual_sales_outside_import_coverage_are_not_flagged(): void
    {
        $coklat = $this->makeProduct('ESK-COK', 'Es Krim Coklat');
        $this->createMapping('shopee', 'es krim coklat', $coklat);

        // Sale on a date NOT covered by any batch
        $this->makeSale('ORD-1', 'shopee', '2026-07-10', $coklat, 1, 30000);

        $this->importBatch('shopee', [
            ['row_number' => 2, 'sale_date' => '2026-07-01', 'external_label' => 'Es Krim Coklat', 'qty' => 1, 'amount' => 30000],
        ]);

        $report = $this->report('shopee', '2026-07-01', '2026-07-15');

        $this->assertNull($this->rowFor($report, '2026-07-10', $coklat->id));
    }

    public function test_voided_sales_and_other_channels_are_excluded(): void
    {
        $coklat = $this->makeProduct('ESK-COK', 'Es Krim Coklat');
        $this->createMapping('shopee', 'es krim coklat', $coklat);

        $voided = $this->makeSale('ORD-VOID', 'shopee', '2026-07-01', $coklat, 5, 30000);
        $voided->update(['voided_at' => now(), 'void_status' => 'approved']);

        $this->makeSale('ORD-TOKPED', 'tokopedia', '2026-07-01', $coklat, 7, 30000);
        $this->makeSale('ORD-OK', 'shopee', '2026-07-01', $coklat, 2, 30000);

        $this->importBatch('shopee', [
            ['row_number' => 2, 'sale_date' => '2026-07-01', 'external_label' => 'Es Krim Coklat', 'qty' => 2, 'amount' => 60000],
        ]);

        $report = $this->report('shopee', '2026-07-01', '2026-07-01');
        $row = $this->rowFor($report, '2026-07-01', $coklat->id);

        $this->assertSame(2, $row['manual_qty']);
        $this->assertSame('match', $row['status']);
    }

    public function test_superseded_batches_are_excluded_from_report(): void
    {
        $coklat = $this->makeProduct('ESK-COK', 'Es Krim Coklat');
        $this->createMapping('shopee', 'es krim coklat', $coklat);

        $this->makeSale('ORD-1', 'shopee', '2026-07-01', $coklat, 2, 30000);

        // First import with wrong qty, then superseding import with correct qty
        $this->importBatch('shopee', [
            ['row_number' => 2, 'sale_date' => '2026-07-01', 'external_label' => 'Es Krim Coklat', 'qty' => 9, 'amount' => 270000],
        ]);
        $this->importBatch('shopee', [
            ['row_number' => 2, 'sale_date' => '2026-07-01', 'external_label' => 'Es Krim Coklat', 'qty' => 2, 'amount' => 60000],
        ], supersede: true);

        $report = $this->report('shopee', '2026-07-01', '2026-07-01');
        $row = $this->rowFor($report, '2026-07-01', $coklat->id);

        $this->assertSame(2, $row['import_qty']);
        $this->assertSame('match', $row['status']);
    }
}
