<?php

namespace App\Invoice;

use App\Invoice\Models\Invoice;
use App\Payment\Payment;
use Exception;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Str;
use Zoomyboy\Tex\Document;
use Zoomyboy\Tex\Engine;
use Zoomyboy\Tex\Template;

abstract class InvoiceDocument extends Document
{
    abstract public function getSubject(): string;
    abstract public function view(): string;
    abstract public function afterSingle(Payment $payment): void;
    abstract public function linkLabel(): string;
    abstract public static function sendAllLabel(): string;

    /**
     * @param HasMany<Payment> $query
     *
     * @return HasMany<Payment>
     */
    abstract public static function paymentsQuery(HasMany $query): HasMany;

    /**
     * @return array<int, string>
     */
    abstract public static function getDescription(): array;

    public string $until;
    public string $filename;

    /**
     * @param array<string, string> $positions
     */
    public function __construct(
        public string $toName,
        public string $toAddress,
        public string $toZip,
        public string $toLocation,
        public string $greeting,
        public array $positions,
        public string $usage,
    ) {
        $this->until = now()->addWeeks(2)->format('d.m.Y');
        $this->filename = Str::slug("{$this->getSubject()} für {$toName}");
    }

    public static function fromInvoice(Invoice $invoice): self
    {
        return static::withoutMagicalCreationFrom([
            'toName' => $invoice->to['name'],
            'toAddress' => $invoice->to['address'],
            'toZip' => $invoice->to['zip'],
            'toLocation' => $invoice->to['location'],
            'greeting' => $invoice->greeting,
            'positions' => static::renderPositions($invoice),
            'usage' => $invoice->usage,
        ]);
    }

    public function settings(): InvoiceSettings
    {
        return app(InvoiceSettings::class);
    }

    public function getEngine(): Engine
    {
        return Engine::PDFLATEX;
    }

    public function basename(): string
    {
        return $this->filename;
    }

    public function template(): Template
    {
        return Template::make('tex.templates.default');
    }

    public function setFilename(string $filename): self
    {
        $this->filename = $filename;

        return $this;
    }

    public function getRecipient(): MailRecipient
    {
        throw_unless($this->email, Exception::class, 'Cannot get Recipient. Mail not set.');

        return new MailRecipient($this->email, $this->familyName);
    }

    /**
     * @return view-string
     */
    public function mailView(): string
    {
        $view = 'mail.payment.' . Str::snake(class_basename($this));

        throw_unless(view()->exists($view), Exception::class, 'Mail view ' . $view . ' existiert nicht.');

        return $view;
    }

    /**
     * @return array<string, string>
     */
    public static function renderPositions(Invoice $invoice): array
    {
        return $invoice->positions->mapWithKeys(fn ($position) => [$position->description => static::number($position->price)])->toArray();
    }

    public static function number(int $number): string
    {
        return number_format($number / 100, 2, '.', '');
    }
}