<?php
declare(strict_types=1);

namespace App\Modules\Procurement\Services;

use App\Core\App;
use App\Core\EventBus;
use App\Core\Logger;
use App\Modules\Inventory\Services\StockService;

final class ReturnToVendorService
{
    public static function createRTV(array $header, array $items): int
    {
        $db = App::getInstance()->db();
        $employee = App::getInstance()->currentEmployee();

        if (empty($items)) {
            throw new \RuntimeException('يجب إضافة صنف واحد على الأقل');
        }

        $supplierId  = (int) ($header['supplier_id'] ?? 0);
        $warehouseId = (int) ($header['warehouse_id'] ?? 0);

        if ($supplierId <= 0) throw new \RuntimeException('يجب تحديد المورد');
        if ($warehouseId <= 0) throw new \RuntimeException('يجب تحديد المخزن');

        $rtvNumber = ProcurementNumberGenerator::nextRTVNumber();
        $totalAmount = '0.00';

        foreach ($items as $item) {
            $qty  = (string) ($item['quantity'] ?? '0');
            $cost = (string) ($item['unit_cost'] ?? '0');
            $lineTotal = bcmul($qty, $cost, 2);
            $totalAmount = bcadd($totalAmount, $lineTotal, 2);
        }

        $db->beginTransaction();
        try {
            $rtvId = $db->insert('return_to_vendor', [
                'rtv_number'        => $rtvNumber,
                'supplier_id'       => $supplierId,
                'grn_id'            => !empty($header['grn_id']) ? (int) $header['grn_id'] : null,
                'purchase_order_id' => !empty($header['purchase_order_id']) ? (int) $header['purchase_order_id'] : null,
                'warehouse_id'      => $warehouseId,
                'reason'            => $header['reason'] ?? '',
                'return_date'       => $header['return_date'] ?? date('Y-m-d'),
                'status'            => 'draft',
                'total_amount'      => $totalAmount,
                'notes'             => $header['notes'] ?? null,
                'branch_id'         => $header['branch_id'] ?? null,
                'created_by'        => $employee ? (int) $employee->id : null,
                'created_at'        => date('Y-m-d H:i:s'),
                'updated_at'        => date('Y-m-d H:i:s'),
            ]);

            foreach ($items as $item) {
                $qty  = (string) ($item['quantity'] ?? '0');
                $cost = (string) ($item['unit_cost'] ?? '0');
                $lineTotal = bcmul($qty, $cost, 2);

                $db->insert('rtv_items', [
                    'rtv_id'       => $rtvId,
                    'item_id'      => (int) $item['item_id'],
                    'grn_item_id'  => !empty($item['grn_item_id']) ? (int) $item['grn_item_id'] : null,
                    'quantity'     => $qty,
                    'unit_cost'    => $cost,
                    'line_total'   => $lineTotal,
                    'batch_number' => $item['batch_number'] ?? null,
                    'reason'       => $item['reason'] ?? null,
                ]);
            }

            $db->commit();
        } catch (\Throwable $e) {
            $db->rollBack();
            throw $e;
        }

        Logger::info("RTV #{$rtvId} ({$rtvNumber}) created, total: {$totalAmount}");
        return $rtvId;
    }

    public static function submitRTV(int $rtvId): void
    {
        $db = App::getInstance()->db();
        $rtv = $db->selectOne("SELECT * FROM `return_to_vendor` WHERE `id` = ?", [$rtvId]);
        if (!$rtv) throw new \RuntimeException('مرتجع المورد غير موجود');
        if ($rtv['status'] !== 'draft') throw new \RuntimeException('المرتجع ليس في حالة مسودة');

        $db->update('return_to_vendor', ['status' => 'submitted', 'updated_at' => date('Y-m-d H:i:s')], '`id` = ?', [$rtvId]);
        Logger::info("RTV #{$rtvId} submitted");
    }

    public static function approveRTV(int $rtvId): void
    {
        $db = App::getInstance()->db();
        $employee = App::getInstance()->currentEmployee();
        $rtv = $db->selectOne("SELECT * FROM `return_to_vendor` WHERE `id` = ?", [$rtvId]);
        if (!$rtv) throw new \RuntimeException('مرتجع المورد غير موجود');
        if ($rtv['status'] !== 'submitted') throw new \RuntimeException('المرتجع ليس في حالة مُقدّم');

        $db->update('return_to_vendor', [
            'status' => 'approved', 'approved_by' => $employee ? (int) $employee->id : null,
            'approved_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'),
        ], '`id` = ?', [$rtvId]);
        Logger::info("RTV #{$rtvId} approved");
    }

    public static function shipRTV(int $rtvId): void
    {
        $db = App::getInstance()->db();
        $employee = App::getInstance()->currentEmployee();
        $rtv = $db->selectOne("SELECT * FROM `return_to_vendor` WHERE `id` = ?", [$rtvId]);
        if (!$rtv) throw new \RuntimeException('مرتجع المورد غير موجود');
        if ($rtv['status'] !== 'approved') throw new \RuntimeException('المرتجع ليس في حالة معتمد');

        $db->update('return_to_vendor', [
            'status' => 'shipped', 'shipped_by' => $employee ? (int) $employee->id : null,
            'shipped_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'),
        ], '`id` = ?', [$rtvId]);
        Logger::info("RTV #{$rtvId} shipped");
    }

    public static function completeRTV(int $rtvId): void
    {
        $db = App::getInstance()->db();
        $employee = App::getInstance()->currentEmployee();

        $rtv = $db->selectOne("SELECT * FROM `return_to_vendor` WHERE `id` = ?", [$rtvId]);
        if (!$rtv) throw new \RuntimeException('مرتجع المورد غير موجود');
        if ($rtv['status'] !== 'shipped') throw new \RuntimeException('المرتجع ليس في حالة شُحن');

        $warehouseId = (int) $rtv['warehouse_id'];

        $db->beginTransaction();
        try {
            // Move stock out for each RTV item
            $rtvItems = $db->select("SELECT * FROM `rtv_items` WHERE `rtv_id` = ?", [$rtvId]);
            foreach ($rtvItems as $item) {
                StockService::moveStock([
                    'item_id'        => (int) $item['item_id'],
                    'warehouse_id'   => $warehouseId,
                    'movement_type'  => 'return_out',
                    'direction'      => 'out',
                    'quantity'       => (string) $item['quantity'],
                    'unit_cost'      => (string) $item['unit_cost'],
                    'reference_type' => 'return_to_vendor',
                    'reference_id'   => $rtvId,
                    'notes'          => 'مرتجع مورد — ' . $rtv['rtv_number'],
                ]);
            }

            $db->update('return_to_vendor', [
                'status' => 'completed', 'completed_by' => $employee ? (int) $employee->id : null,
                'completed_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'),
            ], '`id` = ?', [$rtvId]);

            $db->commit();
        } catch (\Throwable $e) {
            $db->rollBack();
            throw $e;
        }

        EventBus::dispatch('procurement.rtv_completed', [
            'rtv_id'       => $rtvId,
            'supplier_id'  => (int) $rtv['supplier_id'],
            'total_amount' => (string) $rtv['total_amount'],
            'warehouse_id' => $warehouseId,
        ]);

        Logger::info("RTV #{$rtvId} completed — stock moved out, accounting event dispatched");
    }

    public static function cancelRTV(int $rtvId): void
    {
        $db = App::getInstance()->db();
        $rtv = $db->selectOne("SELECT * FROM `return_to_vendor` WHERE `id` = ?", [$rtvId]);
        if (!$rtv) throw new \RuntimeException('مرتجع المورد غير موجود');
        if (!in_array($rtv['status'], ['draft', 'submitted'], true)) throw new \RuntimeException('لا يمكن إلغاء المرتجع في حالته الحالية');

        $db->update('return_to_vendor', ['status' => 'cancelled', 'updated_at' => date('Y-m-d H:i:s')], '`id` = ?', [$rtvId]);
        Logger::info("RTV #{$rtvId} cancelled");
    }
}
