<?php

declare(strict_types=1);

namespace App\Services;

use App\Repositories\QuotationRepository;
use App\Repositories\SalesInvoiceRepository;
use InvalidArgumentException;
use PDO;
use RuntimeException;

final class QuotationService
{
    public function __construct(
        private PDO $pdo,
        private QuotationRepository $quotations,
        private SalesInvoiceRepository $salesInvoices,
    ) {
    }

    public function defaultForm(): array
    {
        return [
            'quotation_no' => next_quotation_no($this->pdo),
            'quotation_date' => date('Y-m-d'),
            'valid_until' => date('Y-m-d', strtotime('+7 days')),
            'party_id' => 0,
            'status' => 'draft',
            'notes' => '',
            'payment_terms' => '',
            'delivery_terms' => '',
            'terms_conditions' => '',
            'customer_mode' => 'existing',
            'quick_customer_name' => '',
            'quick_customer_phone' => '',
            'quick_customer_email' => '',
            'lines' => [
                ['item_id' => 0, 'item_name' => '', 'qty' => 1, 'price' => 0, 'line_total' => 0, 'line_notes' => ''],
            ],
        ];
    }

    public function loadForm(int $id): array
    {
        $form = $this->defaultForm();
        if ($id <= 0) {
            return $form;
        }

        $row = $this->quotations->findById($id);
        if (!$row) {
            throw new RuntimeException('Quotation not found');
        }

        $form = array_merge($form, [
            'quotation_no' => (string) $row['quotation_no'],
            'quotation_date' => (string) $row['quotation_date'],
            'valid_until' => (string) ($row['valid_until'] ?? ''),
            'party_id' => (int) $row['party_id'],
            'status' => (string) $row['status'],
            'notes' => (string) ($row['notes'] ?? ''),
            'payment_terms' => (string) ($row['payment_terms'] ?? ''),
            'delivery_terms' => (string) ($row['delivery_terms'] ?? ''),
            'terms_conditions' => (string) ($row['terms_conditions'] ?? ''),
            'customer_mode' => 'existing',
        ]);

        $lines = $this->quotations->fetchFormItems($id);
        if ($lines) {
            $form['lines'] = [];
            foreach ($lines as $ln) {
                $form['lines'][] = [
                    'item_id' => (int) ($ln['item_id'] ?? 0),
                    'item_name' => (string) ($ln['item_name'] ?? ''),
                    'qty' => (float) ($ln['qty'] ?? 0),
                    'price' => (float) ($ln['unit_price'] ?? 0),
                    'line_total' => (float) ($ln['line_total'] ?? 0),
                    'line_notes' => (string) ($ln['notes'] ?? ''),
                ];
            }
        }

        return $form;
    }

    public function save(int $id, array $input, ?int $currentUserId): int
    {
        $statuses = ['draft','sent','approved','rejected','expired','converted'];
        $form = $this->defaultForm();
        foreach (['quotation_no','quotation_date','valid_until','status','notes','payment_terms','delivery_terms','terms_conditions','customer_mode','quick_customer_name','quick_customer_phone','quick_customer_email'] as $f) {
            $form[$f] = trim((string)($input[$f] ?? $form[$f]));
        }
        $form['party_id'] = (int)($input['party_id'] ?? 0);
        if (!in_array($form['status'], $statuses, true)) {
            $form['status'] = 'draft';
        }
        if (!in_array($form['customer_mode'], ['existing','quick'], true)) {
            $form['customer_mode'] = 'existing';
        }

        [$uiLines, $cleanLines, $subtotal] = $this->normalizeLines($input);
        $form['lines'] = $uiLines;

        if ($form['quotation_no'] === '') {
            throw new InvalidArgumentException('رقم عرض السعر مطلوب.');
        }
        if ($form['quotation_date'] === '') {
            throw new InvalidArgumentException('تاريخ العرض مطلوب.');
        }
        if (!$cleanLines) {
            throw new InvalidArgumentException('أضف صنفًا واحدًا على الأقل.');
        }

        $partyId = $form['party_id'];
        if ($form['customer_mode'] === 'quick' || $partyId <= 0) {
            if ($form['quick_customer_name'] === '') {
                throw new InvalidArgumentException('اسم العميل مطلوب عند التسجيل السريع.');
            }
            if ($form['quick_customer_phone'] === '') {
                throw new InvalidArgumentException('رقم تليفون العميل مطلوب عند التسجيل السريع.');
            }
            $partyId = quotation_create_or_resolve_customer($this->pdo, $form['quick_customer_name'], $form['quick_customer_phone'], $form['quick_customer_email']);
            if ($partyId <= 0) {
                throw new RuntimeException('تعذر إنشاء/تحديد العميل.');
            }
        }

        try {
            $this->pdo->beginTransaction();

            $payload = [
                'quotation_no' => $form['quotation_no'],
                'quotation_date' => $form['quotation_date'],
                'valid_until' => $form['valid_until'] !== '' ? $form['valid_until'] : null,
                'party_id' => $partyId,
                'status' => $form['status'],
                'subtotal' => $subtotal,
                'notes' => $form['notes'] !== '' ? $form['notes'] : null,
                'payment_terms' => $form['payment_terms'] !== '' ? $form['payment_terms'] : null,
                'delivery_terms' => $form['delivery_terms'] !== '' ? $form['delivery_terms'] : null,
                'terms_conditions' => $form['terms_conditions'] !== '' ? $form['terms_conditions'] : null,
                'created_by' => $currentUserId,
            ];

            if ($id > 0) {
                $this->quotations->update($id, $payload);
                audit_log('UPDATE_QUOTATION', 'quotations', $id, ['quotation_no' => $form['quotation_no']]);
                $quotationId = $id;
            } else {
                $quotationId = $this->quotations->create($payload);
                audit_log('CREATE_QUOTATION', 'quotations', $quotationId, ['quotation_no' => $form['quotation_no']]);
            }

            $this->quotations->replaceItems($quotationId, $cleanLines);
            $this->pdo->commit();
            return $quotationId;
        } catch (\Throwable $e) {
            if ($this->pdo->inTransaction()) {
                $this->pdo->rollBack();
            }
            throw $e;
        }
    }

    public function convertToDraftSalesInvoice(int $quotationId, ?int $currentUserId): int
    {
        $quotation = $this->quotations->findById($quotationId);
        if (!$quotation) {
            throw new RuntimeException('Quotation not found');
        }
        $lines = $this->quotations->fetchFormItems($quotationId);
        if (strtolower((string) $quotation['status']) === 'converted' && !empty($quotation['converted_invoice_id'])) {
            return (int) $quotation['converted_invoice_id'];
        }
        if (!$lines) {
            throw new InvalidArgumentException('لا يمكن تحويل عرض سعر بدون بنود.');
        }

        try {
            $this->pdo->beginTransaction();
            $invoiceNo = $this->salesInvoices->generateInvoiceNo();
            $subtotal = (float)($quotation['subtotal'] ?? 0);
            $discountAmount = (float)($quotation['discount_amount'] ?? 0);
            $taxAmount = (float)($quotation['tax_amount'] ?? 0);
            $grandTotal = (float)($quotation['total_amount'] ?? $subtotal);
            $notes = trim((string)($quotation['notes'] ?? ''));
            $notes = ($notes !== '' ? $notes . "\n" : '') . 'تم إنشاؤها من عرض السعر ' . (string)$quotation['quotation_no'];
            $warehouseId = 0;
            try {
                $warehouseId = (int)($this->pdo->query("SELECT id FROM warehouses WHERE is_active=1 ORDER BY id ASC LIMIT 1")->fetchColumn() ?: 0);
            } catch (\Throwable) {
            }

            $invoiceId = $this->salesInvoices->create([
                'invoice_no' => $invoiceNo,
                'invoice_date' => $quotation['quotation_date'],
                'warehouse_id' => $warehouseId > 0 ? $warehouseId : null,
                'party_id' => (int)$quotation['party_id'],
                'status' => 'DRAFT',
                'subtotal' => $subtotal,
                'discount_type' => 'VALUE',
                'discount_value' => $discountAmount,
                'discount_note' => null,
                'discount_amount' => $discountAmount,
                'net_after_discount' => max(0, $subtotal - $discountAmount),
                'vat_enabled' => $taxAmount > 0 ? 1 : 0,
                'vat_rate' => 0,
                'vat_amount' => $taxAmount,
                'grand_total' => $grandTotal,
                'notes' => $notes,
                'payment_mode' => 'credit',
                'paid_amount' => 0,
                'due_amount' => $grandTotal,
                'cashbox_id' => null,
                'collection_note' => null,
                'user_id' => $currentUserId,
                'posted_by' => null,
                'posted_at' => null,
            ]);

            $salesLines = [];
            foreach ($lines as $ln) {
                $salesLines[] = [
                    'item_id' => (int)$ln['item_id'],
                    'qty' => (float)$ln['qty'],
                    'price' => (float)$ln['unit_price'],
                    'line_total' => (float)$ln['line_total'],
                ];
            }
            $itemsMap = $this->salesInvoices->fetchItemsStockMap($warehouseId, array_map(fn(array $r): int => (int)$r['item_id'], $salesLines));
            $this->salesInvoices->replaceLines($invoiceId, $salesLines, $itemsMap);
            $this->quotations->markConverted($quotationId, $invoiceId);

            audit_log('CONVERT_QUOTATION_TO_SALES_INV', 'quotations', $quotationId, ['quotation_no' => $quotation['quotation_no'], 'sales_invoice_id' => $invoiceId]);
            audit_log('CREATE_SALES_INV_FROM_QUOTATION', 'sales_invoices', $invoiceId, ['invoice_no' => $invoiceNo, 'quotation_id' => $quotationId, 'quotation_no' => $quotation['quotation_no']]);

            $this->pdo->commit();
            return $invoiceId;
        } catch (\Throwable $e) {
            if ($this->pdo->inTransaction()) {
                $this->pdo->rollBack();
            }
            throw $e;
        }
    }

    private function normalizeLines(array $input): array
    {
        $itemIds = $input['item_id'] ?? [];
        $itemNames = $input['item_name'] ?? [];
        $qtys = $input['qty'] ?? [];
        $prices = $input['price'] ?? [];
        $lineNotes = $input['line_notes'] ?? [];
        $uiLines = [];
        $cleanLines = [];
        $seen = [];
        $subtotal = 0.0;
        $count = max(count($itemIds), count($itemNames), count($qtys), count($prices));
        for ($i = 0; $i < $count; $i++) {
            $iid = (int)($itemIds[$i] ?? 0);
            $name = trim((string)($itemNames[$i] ?? ''));
            $qty = (float)($qtys[$i] ?? 0);
            $price = (float)($prices[$i] ?? 0);
            $notes = trim((string)($lineNotes[$i] ?? ''));
            $lineTotal = round(max(0, $qty) * max(0, $price), 2);
            $uiLines[] = ['item_id' => $iid, 'item_name' => $name, 'qty' => $qty > 0 ? $qty : 1, 'price' => $price, 'line_total' => $lineTotal, 'line_notes' => $notes];
            if ($iid <= 0 && $name === '' && $qty <= 0 && $price <= 0) {
                continue;
            }
            if ($iid <= 0) {
                throw new InvalidArgumentException('يوجد سطر به صنف غير صحيح أو لم يتم اختياره من القائمة.');
            }
            if ($qty <= 0) {
                throw new InvalidArgumentException('الكمية يجب أن تكون أكبر من صفر في كل سطر.');
            }
            if ($price < 0) {
                throw new InvalidArgumentException('السعر لا يمكن أن يكون سالبًا.');
            }
            if (isset($seen[$iid])) {
                throw new InvalidArgumentException('لا يمكن تكرار نفس الصنف أكثر من مرة داخل عرض السعر.');
            }
            $seen[$iid] = true;
            $subtotal += $lineTotal;
            $cleanLines[] = ['item_id' => $iid, 'qty' => $qty, 'price' => $price, 'line_total' => $lineTotal, 'notes' => $notes !== '' ? $notes : null];
        }
        return [$uiLines, $cleanLines, $subtotal];
    }
}
