<?php

namespace App\Domain\Financial\Services;

use App\Domain\Financial\Enums\InvoiceStatus;
use App\Domain\Financial\Events\InvoiceCancelled;
use App\Domain\Financial\Events\InvoiceCreated;
use App\Domain\Financial\Events\InvoicePaid;
use App\Domain\Financial\Models\Invoice;
use App\Domain\Financial\Models\InvoiceItem;
use App\Models\User;
use Illuminate\Support\Facades\DB;

class InvoiceService
{
    public function create(array $data, array $items, User $creator): Invoice
    {
        return DB::transaction(function () use ($data, $items, $creator) {
            $academyId = $data['academy_id'] ?? app('current_academy')->id;

            $invoice = Invoice::create([
                ...$data,
                'academy_id' => $academyId,
                'number' => $data['number'] ?? $this->generateNumber($academyId),
                'issue_date' => $data['issue_date'] ?? now()->toDateString(),
                'due_date' => $data['due_date'] ?? $data['issue_date'] ?? now()->toDateString(),
                'status' => InvoiceStatus::Draft,
                'created_by' => $creator->id,
                'due_amount' => $data['total_amount'] ?? 0,
            ]);

            foreach ($items as $item) {
                $item['total_amount'] = ($item['quantity'] * $item['unit_price'])
                    - ($item['discount_amount'] ?? 0)
                    + ($item['tax_amount'] ?? 0);

                $invoice->items()->create($item);
            }

            $invoice->recalculateTotals();

            InvoiceCreated::dispatch($invoice, $creator);

            return $invoice->fresh(['items']);
        });
    }

    public function addItem(Invoice $invoice, array $itemData): InvoiceItem
    {
        return DB::transaction(function () use ($invoice, $itemData) {
            $itemData['total_amount'] = ($itemData['quantity'] * $itemData['unit_price'])
                - ($itemData['discount_amount'] ?? 0)
                + ($itemData['tax_amount'] ?? 0);

            $item = $invoice->items()->create($itemData);
            $invoice->recalculateTotals();

            return $item;
        });
    }

    public function cancel(Invoice $invoice, User $user): Invoice
    {
        return DB::transaction(function () use ($invoice, $user) {
            if ($invoice->paid_amount > 0) {
                throw new \App\Domain\Shared\Exceptions\DomainException('لا يمكن إلغاء فاتورة بها مدفوعات');
            }

            if (!in_array($invoice->status, [InvoiceStatus::Draft, InvoiceStatus::Sent, InvoiceStatus::Overdue])) {
                throw new \App\Domain\Shared\Exceptions\DomainException('لا يمكن إلغاء فاتورة بحالة: ' . $invoice->status->label());
            }

            $invoice->update([
                'status' => InvoiceStatus::Cancelled,
                'cancelled_at' => now(),
            ]);

            InvoiceCancelled::dispatch($invoice, $user);

            return $invoice;
        });
    }

    public function markAsPaid(Invoice $invoice, User $actor): void
    {
        $invoice->update([
            'status' => InvoiceStatus::Paid,
            'paid_at' => now(),
            'due_amount' => 0,
        ]);

        InvoicePaid::dispatch($invoice, $actor);
    }

    public function updatePaidAmount(Invoice $invoice, int $amount, User $actor): void
    {
        $invoice->paid_amount += $amount;
        $invoice->due_amount = $invoice->total_amount - $invoice->paid_amount;

        if ($invoice->due_amount <= 0) {
            $invoice->status = $invoice->due_amount < 0
                ? InvoiceStatus::Overpaid
                : InvoiceStatus::Paid;
            $invoice->paid_at = now();
        } else {
            $invoice->status = InvoiceStatus::PartiallyPaid;
        }

        $invoice->save();

        if ($invoice->status === InvoiceStatus::Paid) {
            InvoicePaid::dispatch($invoice, $actor);
        }
    }

    public function generateNumber(int $academyId): string
    {
        $count = Invoice::withoutGlobalScopes()
            ->where('academy_id', $academyId)
            ->count();

        return 'INV-' . str_pad($count + 1, 6, '0', STR_PAD_LEFT);
    }
}
