<?php

declare(strict_types=1);

namespace Maestro\Core\Modules\Offers\Services;

use Illuminate\Support\Facades\DB;
use Maestro\Core\Modules\Offers\Enums\OfferDiscountType;
use Maestro\Core\Modules\Offers\Enums\OfferPricingType;
use Maestro\Core\Modules\Offers\Enums\OfferSectionRole;
use Maestro\Core\Modules\Offers\Models\Offer;
use Maestro\Core\Modules\Offers\Models\OfferRow;
use Maestro\Core\Modules\Offers\Models\OfferSection;

final class OfferStructureSync
{
    /**
     * @param  array<int, array<string, mixed>>  $sections
     */
    public static function replace(Offer $offer, array $sections): void
    {
        DB::transaction(function () use ($offer, $sections): void {
            $offer->sections()->delete();

            foreach ($sections as $sPos => $s) {
                $role = OfferSectionRole::tryFrom((string) ($s['section_role'] ?? ''))
                    ?? OfferSectionRole::Chapter;

                $section = OfferSection::query()->create([
                    'offer_id' => $offer->id,
                    'parent_section_id' => null,
                    'title' => (string) $s['title'],
                    'description_html' => isset($s['description_html']) ? (string) $s['description_html'] : null,
                    'sort_order' => isset($s['sort_order']) ? (int) $s['sort_order'] : $sPos,
                    'section_role' => $role,
                ]);

                if ($role === OfferSectionRole::PricingRoot) {
                    foreach (($s['children'] ?? []) as $cPos => $c) {
                        $child = OfferSection::query()->create([
                            'offer_id' => $offer->id,
                            'parent_section_id' => $section->id,
                            'title' => (string) ($c['title'] ?? 'Category'),
                            'description_html' => isset($c['description_html']) ? (string) $c['description_html'] : null,
                            'sort_order' => isset($c['sort_order']) ? (int) $c['sort_order'] : $cPos,
                            'section_role' => OfferSectionRole::PricingCategory,
                        ]);

                        self::insertRows($child, $c['rows'] ?? []);
                    }

                    continue;
                }

                self::insertRows($section, $s['rows'] ?? []);
            }

            OfferTotalsService::persistOfferVat($offer->fresh(['rootSections.children.rows', 'rootSections.rows']));
        });
    }

    /**
     * @param  array<int, array<string, mixed>>  $rows
     */
    private static function insertRows(OfferSection $section, array $rows): void
    {
        foreach ($rows as $rPos => $r) {
            $pricing = OfferPricingType::tryFrom((string) ($r['pricing_type'] ?? 'one_time')) ?? OfferPricingType::OneTime;
            $discountT = OfferDiscountType::tryFrom((string) ($r['discount_type'] ?? 'none')) ?? OfferDiscountType::None;

            OfferRow::query()->create([
                'offer_section_id' => $section->id,
                'offer_service_catalog_item_id' => $r['offer_service_catalog_item_id'] ?? null,
                'description_html' => isset($r['description_html']) ? (string) $r['description_html'] : null,
                'pricing_type' => $pricing->value,
                'custom_unit_label' => isset($r['custom_unit_label']) ? (string) $r['custom_unit_label'] : null,
                'quantity' => (float) ($r['quantity'] ?? 1),
                'unit_price' => (float) ($r['unit_price'] ?? 0),
                'discount_type' => $discountT->value,
                'discount_value' => (float) ($r['discount_value'] ?? 0),
                'vat_rate' => (float) ($r['vat_rate'] ?? 0),
                'sort_order' => isset($r['sort_order']) ? (int) $r['sort_order'] : $rPos,
            ]);
        }
    }

    /** Copy sections/rows from source offer onto a blank target draft. */
    public static function copyFromOffer(Offer $source, Offer $target): void
    {
        $source->loadMissing(['rootSections.children.rows', 'rootSections.rows']);
        $payload = [];

        foreach ($source->rootSections as $section) {
            $role = $section->section_role instanceof OfferSectionRole
                ? $section->section_role
                : OfferSectionRole::tryFrom((string) $section->section_role) ?? OfferSectionRole::Chapter;

            if ($role === OfferSectionRole::PricingRoot) {
                $children = [];
                foreach ($section->children as $child) {
                    $rows = [];
                    foreach ($child->rows as $row) {
                        $rows[] = self::rowPayloadFromModel($row);
                    }
                    $children[] = [
                        'title' => $child->title,
                        'description_html' => $child->description_html,
                        'sort_order' => $child->sort_order,
                        'section_role' => OfferSectionRole::PricingCategory->value,
                        'rows' => $rows,
                    ];
                }

                $payload[] = [
                    'title' => $section->title,
                    'description_html' => $section->description_html,
                    'sort_order' => $section->sort_order,
                    'section_role' => OfferSectionRole::PricingRoot->value,
                    'children' => $children,
                ];

                continue;
            }

            $rows = [];
            foreach ($section->rows as $row) {
                $rows[] = self::rowPayloadFromModel($row);
            }

            $payload[] = [
                'title' => $section->title,
                'description_html' => $section->description_html,
                'sort_order' => $section->sort_order,
                'section_role' => $role->value,
                'rows' => $rows,
            ];
        }

        self::replace($target, $payload);
    }

    private static function rowPayloadFromModel(OfferRow $row): array
    {
        return [
            'description_html' => $row->description_html,
            'pricing_type' => $row->pricing_type?->value,
            'custom_unit_label' => $row->custom_unit_label,
            'quantity' => $row->quantity,
            'unit_price' => $row->unit_price,
            'discount_type' => $row->discount_type?->value,
            'discount_value' => $row->discount_value,
            'vat_rate' => $row->vat_rate,
            'offer_service_catalog_item_id' => $row->offer_service_catalog_item_id,
            'sort_order' => $row->sort_order,
        ];
    }
}
