<?php

namespace App\Services\Finance;

use App\Models\Invoice;
use App\Models\InvoiceLine;
use App\Models\Quotation;
use Illuminate\Support\Facades\DB;
use InvalidArgumentException;

class QuotationService
{
    public function __construct(
        private readonly DocumentNumberService $documentNumbers,
        private readonly InvoiceService $invoiceService,
    ) {}

    /**
     * @param  array<string, mixed>  $data
     * @param  array<int, array<string, mixed>>  $lines
     */
    public function create(array $data, array $lines): Quotation
    {
        if ($lines === []) {
            throw new InvalidArgumentException('A quotation requires at least one line item.');
        }

        return DB::transaction(function () use ($data, $lines) {
            $subtotal = '0.00';

            foreach ($lines as $line) {
                $lineTotal = bcmul((string) $line['quantity'], (string) $line['unit_price'], 2);
                $subtotal = bcadd($subtotal, $lineTotal, 2);
            }

            $taxAmount = (string) ($data['tax_amount'] ?? '0.00');
            $totalAmount = bcadd($subtotal, $taxAmount, 2);

            $quotation = Quotation::query()->create([
                'quotation_number' => $this->documentNumbers->next('QUO', Quotation::class, 'quotation_number'),
                'student_id' => $data['student_id'] ?? null,
                'customer_name' => $data['customer_name'] ?? null,
                'issue_date' => $data['issue_date'],
                'valid_until' => $data['valid_until'] ?? null,
                'status' => 'draft',
                'subtotal' => $subtotal,
                'tax_amount' => $taxAmount,
                'total_amount' => $totalAmount,
                'notes' => $data['notes'] ?? null,
            ]);

            foreach ($lines as $line) {
                $lineTotal = bcmul((string) $line['quantity'], (string) $line['unit_price'], 2);

                $quotation->lines()->create([
                    'description' => $line['description'],
                    'quantity' => $line['quantity'],
                    'unit_price' => $line['unit_price'],
                    'line_total' => $lineTotal,
                ]);
            }

            return $quotation->fresh(['lines', 'student']);
        });
    }

    public function convertToInvoice(Quotation $quotation, int $studentId, ?int $defaultAccountId = null): Invoice
    {
        $quotation->loadMissing('lines');

        if ($quotation->status === 'converted') {
            throw new InvalidArgumentException('Quotation has already been converted.');
        }

        if ($quotation->lines->isEmpty()) {
            throw new InvalidArgumentException('Cannot convert a quotation without line items.');
        }

        return DB::transaction(function () use ($quotation, $studentId, $defaultAccountId) {
            $invoice = Invoice::query()->create([
                'invoice_number' => $this->documentNumbers->next('INV', Invoice::class, 'invoice_number'),
                'student_id' => $studentId,
                'issue_date' => now()->toDateString(),
                'due_date' => $quotation->valid_until,
                'status' => 'draft',
                'subtotal' => $quotation->subtotal,
                'tax_amount' => $quotation->tax_amount,
                'total_amount' => $quotation->total_amount,
                'amount_paid' => 0,
                'balance_due' => $quotation->total_amount,
                'notes' => $quotation->notes,
            ]);

            foreach ($quotation->lines as $line) {
                InvoiceLine::query()->create([
                    'invoice_id' => $invoice->id,
                    'description' => $line->description,
                    'quantity' => $line->quantity,
                    'unit_price' => $line->unit_price,
                    'line_total' => $line->line_total,
                    'account_id' => $defaultAccountId,
                ]);
            }

            $quotation->update(['status' => 'converted']);

            return $this->invoiceService->issue($invoice->fresh(['lines', 'student']));
        });
    }
}
