<?php

declare(strict_types=1);

namespace Maestro\Core\Modules\Offers\Services;

use Illuminate\Support\Facades\Storage;
use Maestro\Core\Models\MaestroAppSetting;
use Maestro\Core\Models\Organization;
use Maestro\Core\Modules\Offers\Enums\OfferSectionRole;
use Maestro\Core\Modules\Offers\Models\Offer;
use Maestro\Core\Modules\Offers\Models\OfferRow;
use Maestro\Core\Support\MaestroAppSettingsPresenter;

final class OfferDocumentBuilder
{
    /**
     * @return array<string, mixed>
     */
    public static function build(Offer $offer): array
    {
        $offer->loadMissing([
            'customer',
            'contactPerson',
            'rootSections.rows.catalogItem',
            'rootSections.children.rows.catalogItem',
        ]);

        $organization = Organization::instance();
        $totals = OfferTotalsService::summarize($offer);
        $app = MaestroAppSettingsPresenter::toArray(MaestroAppSetting::instance());

        return [
            'offer_number' => $offer->offer_number,
            'subject' => $offer->subject,
            'intro_html' => $offer->intro_html,
            'status' => $offer->status?->value,
            'valid_until' => optional($offer->valid_until)?->format('Y-m-d'),
            'issued_on' => $offer->created_at?->toDateString(),
            'signer_name' => $offer->signer_name,
            'signature_url' => self::signatureUrl($offer->signature_path),

            'customer' => $offer->customer ? [
                'name' => $offer->customer->name,
                'email' => $offer->customer->email,
                'phone' => $offer->customer->phone,
            ] : null,

            'contact' => self::contactBlock($offer),

            'sender' => $organization ? self::senderBlock($organization) : [],

            'logo_url' => $app['logos']['header'],

            'sections' => $offer->rootSections->map(function ($section): array {
                $role = $section->section_role instanceof OfferSectionRole
                    ? $section->section_role
                    : OfferSectionRole::tryFrom((string) $section->section_role) ?? OfferSectionRole::Chapter;

                if ($role === OfferSectionRole::PricingRoot) {
                    $categories = $section->children->map(function ($child): array {
                        return [
                            'title' => $child->title,
                            'description_html' => $child->description_html,
                            'rows' => self::mapRowsToDocument($child->rows),
                        ];
                    })->values()->all();

                    return [
                        'title' => $section->title,
                        'description_html' => $section->description_html,
                        'is_pricing_block' => true,
                        'categories' => $categories,
                        'rows' => [],
                    ];
                }

                return [
                    'title' => $section->title,
                    'description_html' => $section->description_html,
                    'is_pricing_block' => false,
                    'categories' => [],
                    'rows' => self::mapRowsToDocument($section->rows),
                ];
            })->values()->all(),

            'totals' => $totals,
        ];
    }

    /** @param  Offer  $offer  model with contact relation */
    private static function contactBlock(Offer $offer): ?array
    {
        $c = $offer->contactPerson;

        return $c ? [
            'name' => trim($c->first_name.' '.$c->last_name),
            'email' => $c->email,
            'phone' => $c->phone,
        ] : null;
    }

    /**
     * @return array<string, string|null>
     */
    private static function senderBlock(Organization $org): array
    {
        $line1 = trim(implode(' ', array_filter([(string) $org->street, (string) $org->house_number])));
        $line2Parts = [$org->postal_code, $org->city, $org->country];
        $line2 = trim(implode(' ', array_filter($line2Parts)));

        return [
            'legal_name' => $org->display_name,
            'address_line_1' => $line1 !== '' ? $line1 : (string) $org->address_line_1,
            'address_line_2' => $line2 !== '' ? $line2 : trim((string) $org->address_line_2),
            'vat_number' => $org->vat_number,
            'iban' => $org->iban,
            'email' => $org->email,
            'phone' => $org->phone,
            'invoice_email' => $org->invoice_email,
            'website' => $org->website,
        ];
    }

    private static function signatureUrl(?string $path): ?string
    {
        if ($path === null || $path === '') {
            return null;
        }

        return Storage::disk('public')->url($path);
    }

    /**
     * @param  iterable<OfferRow>  $rows
     * @return list<array<string, mixed>>
     */
    private static function mapRowsToDocument(iterable $rows): array
    {
        $rowsOut = [];
        foreach ($rows as $row) {
            [$net, $vatLine] = OfferTotalsService::lineAmounts($row);
            $rowsOut[] = [
                'description_html' => $row->description_html,
                'pricing_type' => $row->pricing_type?->value,
                'pricing_label' => OfferTotalsService::pricingLabel($row->pricing_type),
                'custom_unit_label' => $row->custom_unit_label,
                'quantity' => (float) $row->quantity,
                'unit_price' => (float) $row->unit_price,
                'vat_rate' => (float) $row->vat_rate,
                'line_net_total' => $net,
                'line_vat' => $vatLine,
                'discount_type' => $row->discount_type?->value,
                'discount_value' => (float) $row->discount_value,
            ];
        }

        return $rowsOut;
    }
}
