<?php

declare(strict_types=1);

namespace App\Services;

use App\Models\Invoice;
use App\Models\InvoiceItem;
use App\Models\Order;
use App\Models\Project;
use App\Models\User;
use App\Models\WalletTransaction;
use App\Notifications\SimpleDatabaseNotification;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

class InvoiceService
{
    public function __construct(private readonly WalletService $wallet) {}

    public function generateNumber(): string
    {
        return 'INV-' . now()->format('Ymd') . '-' . Str::upper(Str::random(6));
    }

    /**
     * @param  array<int, array<string, mixed>>  $items
     */
    public function syncItems(Invoice $invoice, array $items): int
    {
        $invoice->items()->delete();

        $total = 0;
        foreach ($items as $item) {
            $qty = (float) ($item['quantity'] ?? 1);
            $unitPrice = (int) ($item['unit_price'] ?? 0);
            $lineTotal = isset($item['total']) ? (int) $item['total'] : (int) round($qty * $unitPrice);

            InvoiceItem::create([
                'invoice_id' => $invoice->id,
                'name' => $item['name'],
                'category' => $item['category'] ?? null,
                'subcategory' => $item['subcategory'] ?? null,
                'quantity' => $qty,
                'unit' => $item['unit'] ?? null,
                'unit_price' => $unitPrice,
                'total' => $lineTotal,
                'description' => $item['description'] ?? null,
                'attachments' => $item['attachments'] ?? null,
            ]);

            $total += $lineTotal;
        }

        $transport = (int) data_get($invoice->meta, 'transport.amount', 0);
        $invoice->update(['total' => $total + $transport]);

        return (int) $invoice->fresh()->total;
    }

    public function calculateLateFee(Invoice $invoice, ?\Carbon\CarbonInterface $asOf = null): int
    {
        if (! in_array($invoice->status, [Invoice::STATUS_UNPAID, Invoice::STATUS_APPROVED], true)) {
            return (int) $invoice->late_fee_amount;
        }

        if (! $invoice->due_at) {
            return 0;
        }

        $asOf = $asOf ?? now();
        $graceDays = (int) config('finance.late_fee_grace_days', 7);
        $rate = (float) config('finance.late_fee_daily_rate', 0.001);
        $delayStart = $invoice->due_at->copy()->addDays($graceDays);

        if ($asOf->lt($delayStart)) {
            return 0;
        }

        $delayDays = $delayStart->diffInDays($asOf);
        if ($delayDays <= 0) {
            return 0;
        }

        return (int) round(((int) $invoice->total) * $rate * $delayDays);
    }

    public function refreshLateFee(Invoice $invoice): Invoice
    {
        $fee = $this->calculateLateFee($invoice);
        if ((int) $invoice->late_fee_amount !== $fee) {
            $invoice->update(['late_fee_amount' => $fee]);
        }

        return $invoice->fresh();
    }

    /**
     * Employers (buyers) on a project via paid/active orders.
     *
     * @return Collection<int, User>
     */
    public function projectEmployers(Project $project): Collection
    {
        $userIds = Order::query()
            ->where('project_id', $project->id)
            ->whereNotNull('user_id')
            ->pluck('user_id')
            ->unique()
            ->filter();

        if ($userIds->isEmpty() && $project->user_id) {
            $userIds = collect([$project->user_id]);
        }

        return User::query()->whereIn('id', $userIds)->get();
    }

    public function approve(Invoice $invoice, ?User $admin = null): Invoice
    {
        return DB::transaction(function () use ($invoice, $admin) {
            $invoice->loadMissing(['project.producer', 'user', 'items']);

            $commission = $invoice->commission_percent !== null
                ? (float) $invoice->commission_percent
                : (float) config('finance.commission_percent', 5);

            if ($invoice->type === Invoice::TYPE_EXPENSE) {
                $invoice->update([
                    'status' => Invoice::STATUS_UNPAID,
                    'commission_percent' => $commission,
                    'admin_reason' => null,
                    'issued_at' => $invoice->issued_at ?? now()->toDateString(),
                    'due_at' => $invoice->due_at ?? now()->addDays(7)->toDateString(),
                ]);

                $this->notifyRecipients(
                    $invoice,
                    'اعلان پرداخت فاکتور',
                    'فاکتور هزینه «' . ($invoice->title ?: $invoice->number) . '» صادر شد و نیازمند پرداخت است.'
                );
            } else {
                $invoice->update([
                    'status' => Invoice::STATUS_APPROVED,
                    'commission_percent' => $commission,
                    'admin_reason' => null,
                    'issued_at' => $invoice->issued_at ?? now()->toDateString(),
                ]);

                $this->distributeIncome($invoice, $commission);

                $this->notifyRecipients(
                    $invoice,
                    'اعلان واریز سود',
                    'فاکتور درآمد «' . ($invoice->title ?: $invoice->number) . '» تایید و به کیف پول واریز شد.'
                );
            }

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

    public function reject(Invoice $invoice, string $reason): Invoice
    {
        $invoice->update([
            'status' => Invoice::STATUS_REJECTED,
            'admin_reason' => $reason,
        ]);

        $this->notifyCreator(
            $invoice,
            'رد فاکتور',
            'فاکتور «' . ($invoice->title ?: $invoice->number) . '» رد شد. دلیل: ' . $reason
        );

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

    public function needsEdit(Invoice $invoice, string $reason): Invoice
    {
        $invoice->update([
            'status' => Invoice::STATUS_NEEDS_EDIT,
            'admin_reason' => $reason,
        ]);

        $this->notifyCreator(
            $invoice,
            'نیاز به ویرایش فاکتور',
            'فاکتور «' . ($invoice->title ?: $invoice->number) . '» نیاز به ویرایش دارد. دلیل: ' . $reason
        );

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

    private function distributeIncome(Invoice $invoice, float $commissionPercent): void
    {
        $recipients = collect();

        if ($invoice->project_id) {
            $recipients = $this->projectEmployers($invoice->project);
        }

        if ($recipients->isEmpty() && $invoice->user_id) {
            $recipients = collect([User::find($invoice->user_id)])->filter();
        }

        if ($recipients->isEmpty()) {
            return;
        }

        $gross = (int) $invoice->total;
        $share = (int) floor($gross / max($recipients->count(), 1));
        $commissionPerShare = (int) round($share * ($commissionPercent / 100));
        $net = max($share - $commissionPerShare, 0);

        foreach ($recipients as $user) {
            if ($net > 0) {
                $this->wallet->credit(
                    $user,
                    $net,
                    WalletTransaction::TYPE_PROFIT,
                    'واریز سود فاکتور ' . ($invoice->number ?? $invoice->id),
                    ['commission_percent' => $commissionPercent],
                    projectId: $invoice->project_id,
                    invoiceId: $invoice->id,
                );
            }

            if ($commissionPerShare > 0) {
                WalletTransaction::create([
                    'user_id' => $user->id,
                    'type' => WalletTransaction::TYPE_COMMISSION,
                    'amount' => $commissionPerShare,
                    'balance_after' => (int) $user->fresh()->wallet,
                    'description' => 'کارمزد فاکتور ' . ($invoice->number ?? $invoice->id),
                    'status' => WalletTransaction::STATUS_COMPLETED,
                    'project_id' => $invoice->project_id,
                    'invoice_id' => $invoice->id,
                    'meta' => ['note' => 'کسر کارمزد از سهم درآمد'],
                ]);
            }
        }
    }

    private function notifyCreator(Invoice $invoice, string $title, string $body): void
    {
        $user = $invoice->createdBy ?? $invoice->user;
        if ($user) {
            $user->notify(new SimpleDatabaseNotification($title, $body, [
                'type' => 'invoice',
                'invoice_id' => $invoice->id,
            ]));
        }
    }

    private function notifyRecipients(Invoice $invoice, string $title, string $body): void
    {
        if ($invoice->project) {
            foreach ($this->projectEmployers($invoice->project) as $user) {
                $user->notify(new SimpleDatabaseNotification($title, $body, [
                    'type' => 'invoice',
                    'invoice_id' => $invoice->id,
                ]));
            }
        } elseif ($invoice->user) {
            $invoice->user->notify(new SimpleDatabaseNotification($title, $body, [
                'type' => 'invoice',
                'invoice_id' => $invoice->id,
            ]));
        }
    }
}
