<?php

declare(strict_types=1);

namespace App\Services;

use App\Repositories\PurchaseReturnRepository;
use InvalidArgumentException;
use PDO;
use Throwable;

final class PurchaseReturnService
{
    public function __construct(private PDO $pdo, private PurchaseReturnRepository $repo)
    {
    }

    public function ensureSchemaOrThrow(): void
    {
        if (!table_exists($this->pdo, 'purchase_returns')) {
            throw new InvalidArgumentException('مطلوب تشغيل ملف التحديث: sql/migrate_v1_23_purchases.sql');
        }
    }

    public function loadForm(int $returnId, int $sourceInvoiceId): array
    {
        $isEdit = $returnId > 0;
        $return = null;
        if ($isEdit) {
            $return = $this->repo->findById($returnId);
            if (!$return) throw new InvalidArgumentException('Not found');
            if (strtoupper((string)$return['status']) !== 'DRAFT') throw new InvalidArgumentException('لا يمكن تعديل مرتجع غير مسودة.');
            $sourceInvoiceId = (int)$return['source_invoice_id'];
        }
        if ($sourceInvoiceId <= 0) throw new InvalidArgumentException('invoice_id required');
        $invoice = $this->repo->fetchSourceInvoice($sourceInvoiceId);
        if (!$invoice) throw new InvalidArgumentException('Invoice not found');
        if (strtoupper((string)$invoice['status']) !== 'POSTED') throw new InvalidArgumentException('يجب أن تكون فاتورة الشراء مُرحّلة لإنشاء مرتجع.');

        $draftMap = [];
        if ($isEdit) {
            foreach ($this->repo->fetchDraftLines($returnId) as $dl) {
                $draftMap[((int)$dl['item_id']) . ':' . ((int)($dl['unit_id'] ?? 0))] = $dl;
            }
        }
        $returnedMap = $this->repo->fetchPostedReturnedQtyMap($sourceInvoiceId, $isEdit ? $returnId : null);
        $lines = [];
        foreach ($this->repo->fetchInvoiceLinesForReturn($sourceInvoiceId) as $ln) {
            $key = ((int)$ln['item_id']) . ':' . ((int)($ln['unit_id'] ?? 0));
            $maxQty = max(0.0, (float)$ln['qty'] - (float)($returnedMap[$key] ?? 0.0));
            $curQty = isset($draftMap[$key]) ? (float)($draftMap[$key]['qty'] ?? 0) : 0.0;
            if ($curQty > $maxQty) $maxQty = $curQty;
            $lines[] = [
                'item_id' => (int)$ln['item_id'],
                'unit_id' => (int)($ln['unit_id'] ?? 0),
                'item_name' => (string)$ln['item_name'],
                'unit_name' => (string)($ln['unit_name'] ?? ''),
                'max_qty' => $maxQty,
                'qty' => $curQty,
                'unit_price' => isset($draftMap[$key]) ? (float)($draftMap[$key]['unit_price'] ?? $ln['unit_price']) : (float)$ln['unit_price'],
            ];
        }
        $return = $return ?: ['ret_date' => date('Y-m-d'), 'notes' => ''];
        return compact('return','lines','invoice','isEdit','returnId');
    }

    public function saveDraft(int $returnId, int $sourceInvoiceId, array $input, int $userId): int
    {
        $form = $this->loadForm($returnId, $sourceInvoiceId);
        $retDate = trim((string)($input['ret_date'] ?? '')) ?: date('Y-m-d');
        $notes = trim((string)($input['notes'] ?? '')) ?: null;
        $itemIds = $input['item_id'] ?? [];
        $unitIds = $input['unit_id'] ?? [];
        $maxQtys = $input['max_qty'] ?? [];
        $qtys = $input['qty'] ?? [];
        $prices = $input['unit_price'] ?? [];
        $newLines = [];
        $subtotal = 0.0;
        for ($i=0; $i<count($itemIds) && $i<300; $i++) {
            $iid = (int)($itemIds[$i] ?? 0); if ($iid <= 0) continue;
            $uid = (int)($unitIds[$i] ?? 0);
            $maxq = (float)($maxQtys[$i] ?? 0);
            $qty = (float)($qtys[$i] ?? 0); if ($qty <= 0) continue;
            $price = max(0, (float)($prices[$i] ?? 0));
            if ($maxq > 0 && $qty > $maxq + 0.0001) throw new InvalidArgumentException('كمية مرتجع أكبر من المتاح للصنف.');
            $lineTotal = $qty * $price; $subtotal += $lineTotal;
            $newLines[] = ['item_id'=>$iid,'unit_id'=>$uid>0?$uid:null,'qty'=>$qty,'unit_price'=>$price,'line_total'=>$lineTotal];
        }
        if (!$newLines) throw new InvalidArgumentException('أدخل كمية مرتجع على الأقل لصنف واحد.');

        $this->pdo->beginTransaction();
        try {
            if ($returnId <= 0) {
                $returnId = $this->repo->createDraft([
                    'ret_no' => $this->repo->generateReturnNo(),
                    'party_id' => (int)($form['invoice']['party_id'] ?: $form['invoice']['supplier_id']),
                    'warehouse_id' => (int)$form['invoice']['warehouse_id'],
                    'ret_date' => $retDate,
                    'source_invoice_id' => $sourceInvoiceId,
                    'notes' => $notes,
                    'subtotal' => round($subtotal,2),
                    'total' => round($subtotal,2),
                ], $userId);
                audit_log('CREATE_PURCHASE_RET', 'purchase_returns', $returnId, ['invoice_id'=>$sourceInvoiceId]);
            } else {
                $ret = $this->repo->findById($returnId);
                if (!$ret) throw new InvalidArgumentException('Not found');
                if (strtoupper((string)$ret['status']) !== 'DRAFT') throw new InvalidArgumentException('لا يمكن تعديل مرتجع غير مسودة.');
                $this->repo->updateDraft($returnId, ['ret_date'=>$retDate,'notes'=>$notes,'subtotal'=>round($subtotal,2),'total'=>round($subtotal,2)]);
                audit_log('UPDATE_PURCHASE_RET', 'purchase_returns', $returnId);
            }
            $this->repo->replaceLines($returnId, $newLines);
            $this->pdo->commit();
            return $returnId;
        } catch (Throwable $e) {
            if ($this->pdo->inTransaction()) $this->pdo->rollBack();
            throw $e;
        }
    }

    public function post(int $id, int $userId): void
    {
        $this->pdo->beginTransaction();
        try {
            $ret = $this->repo->lock($id);
            if (!$ret) throw new InvalidArgumentException('Not found');
            if (strtoupper((string)$ret['status']) !== 'DRAFT') throw new InvalidArgumentException('لا يمكن ترحيل مرتجع غير مسودة.');
            $lines = $this->repo->fetchLines($id);
            if (!$lines) throw new InvalidArgumentException('لا توجد أصناف.');
            $warehouseId = (int)$ret['warehouse_id'];
            $allowNeg = warehouse_allows_negative($this->pdo, $warehouseId);
            if (!$allowNeg) {
                $need = [];
                foreach ($lines as $ln) {
                    $iid = (int)$ln['item_id'];
                    $uid = (int)($ln['unit_id'] ?? 0);
                    $qtyBase = (float)$ln['qty'] * item_unit_factor($this->pdo, $iid, $uid > 0 ? $uid : null);
                    $need[$iid] = ($need[$iid] ?? 0.0) + $qtyBase;
                }
                if ($need) {
                    $ids = array_keys($need);
                    $in = implode(',', array_fill(0, count($ids), '?'));
                    $st = $this->pdo->prepare("SELECT item_id, COALESCE(qty,0) qty FROM stock_balances WHERE warehouse_id=? AND item_id IN ($in)");
                    $st->execute(array_merge([$warehouseId], $ids));
                    $have = [];
                    foreach ($st->fetchAll() ?: [] as $r) $have[(int)$r['item_id']] = (float)$r['qty'];
                    foreach ($need as $iid => $q) {
                        $h = $have[(int)$iid] ?? 0.0;
                        if ($h < $q - 0.0001) throw new InvalidArgumentException('رصيد غير كافٍ لبعض الأصناف.');
                    }
                }
            }
            $this->repo->deletePostingEffects($id);
            foreach ($lines as $ln) {
                $itemId = (int)$ln['item_id'];
                $unitId = (int)($ln['unit_id'] ?? 0);
                $qtyBase = (float)$ln['qty'] * item_unit_factor($this->pdo, $itemId, $unitId > 0 ? $unitId : null);
                $price = (float)$ln['unit_price'];
                $this->pdo->prepare("INSERT INTO stock_moves (ref_type, ref_id, warehouse_id, item_id, qty_in, qty_out, cost, user_id) VALUES ('PURCHASE_RET', ?, ?, ?, 0, ?, ?, ?)")
                    ->execute([$id, $warehouseId, $itemId, $qtyBase, $price, $userId]);
                if ($allowNeg) {
                    stock_balance_apply($this->pdo, $warehouseId, $itemId, -$qtyBase);
                } else {
                    stock_balance_deduct_or_fail($this->pdo, $warehouseId, $itemId, $qtyBase);
                }
            }
            $partyId = (int)($ret['party_id'] ?? $ret['supplier_id'] ?? 0);
            if ($partyId > 0) {
                party_add_ledger($this->pdo, $partyId, 'PURCHASE_RET', $id, (string)$ret['ret_no'], (string)$ret['ret_date'], (float)$ret['total'], 0.0, trim((string)($ret['notes'] ?? '')) !== '' ? (string)$ret['notes'] : 'مرتجع شراء');
            }
            $this->repo->markPosted($id, $userId);
            audit_log('POST_PURCHASE_RET', 'purchase_returns', $id, ['ret_no'=>$ret['ret_no'] ?? null]);
            $this->pdo->commit();
        } catch (Throwable $e) {
            if ($this->pdo->inTransaction()) $this->pdo->rollBack();
            throw $e;
        }
    }

    public function cancelDraft(int $id): void
    {
        $this->repo->markCanceledDraft($id);
        audit_log('CANCEL_PURCHASE_RET', 'purchase_returns', $id);
    }
}
