<?php

declare(strict_types=1);

namespace App\Business\MasterData;

use App\Business\MasterData\Enums\RegionLevel;
use App\Models\DberpRegion;
use Illuminate\Support\Facades\Schema as SchemaFacade;
use Illuminate\Validation\ValidationException;

/**
 * 组装手工新增地区写入 {@see DberpRegion} 的载荷（系统字段由本服务计算）。
 */
final class DberpManualRegionCreationService
{
    public function __construct(
        private readonly DberpCustomRegionCodeService $customRegionCodeService,
        private readonly DberpRegionAddressService $regionAddressService,
    ) {}

    /**
     * @param  array<string, mixed>  $data
     * @return array<string, mixed>
     */
    public function buildCreatePayload(array $data): array
    {
        $name = (string) ($data['name'] ?? '');
        $name = trim($name);

        if ($name === '') {
            throw ValidationException::withMessages([
                'name' => __('dberp.errors.region_name_required'),
            ]);
        }

        $parentId = $data['parent_id'] ?? null;
        $parentId = $parentId === null || $parentId === '' ? null : (int) $parentId;

        $parent = $parentId === null
            ? null
            : DberpRegion::query()->find($parentId);

        if ($parentId !== null && ! $parent instanceof DberpRegion) {
            throw ValidationException::withMessages([
                'parent_id' => __('dberp.errors.region_manual_parent_not_found'),
            ]);
        }

        if ($parent instanceof DberpRegion && ! $parent->is_enabled) {
            throw ValidationException::withMessages([
                'parent_id' => __('dberp.errors.region_manual_parent_disabled'),
            ]);
        }

        $countryCode = $this->resolveCountryCode($parent);
        $regionCode = $this->customRegionCodeService->allocateNext($parent, $countryCode);

        $level = $this->resolveLevel($parent);
        $depth = $this->resolveDepth($parent);
        $path = $this->resolvePath($parent, $regionCode);
        $mergerName = $this->resolveMergerName($parent, $name);

        $maxOrder = (int) DberpRegion::query()
            ->when(
                $parent instanceof DberpRegion,
                fn ($query) => $query->where('parent_id', $parent->getKey()),
                fn ($query) => $query->whereNull('parent_id'),
            )
            ->max('sort_order');

        $payload = [
            'parent_id' => $parent instanceof DberpRegion ? $parent->getKey() : null,
            'country_code' => $countryCode,
            'region_code' => $regionCode,
            'external_code' => null,
            'name' => $name,
            'short_name' => filled($data['short_name'] ?? null) ? trim((string) $data['short_name']) : null,
            'level' => $level,
            'depth' => $depth,
            'path' => $path,
            'merger_name' => $mergerName,
            'sort_order' => (int) ($data['sort_order'] ?? ($maxOrder + 1)),
            'is_enabled' => (bool) ($data['is_enabled'] ?? true),
            'is_leaf' => true,
            'source' => DberpRegionSource::Manual,
        ];

        if (SchemaFacade::hasColumn((new DberpRegion)->getTable(), 'remark')) {
            $payload['remark'] = filled($data['remark'] ?? null) ? (string) $data['remark'] : null;
        }

        return $payload;
    }

    private function resolveCountryCode(?DberpRegion $parent): string
    {
        if ($parent instanceof DberpRegion && filled($parent->country_code)) {
            return (string) $parent->country_code;
        }

        $default = $this->regionAddressService->defaultCountryCode();

        if (filled($default)) {
            return (string) $default;
        }

        return 'CN';
    }

    private function resolveLevel(?DberpRegion $parent): string
    {
        if (! $parent instanceof DberpRegion) {
            return RegionLevel::Country->value;
        }

        $parentLevel = RegionLevel::tryFrom((string) $parent->level) ?? RegionLevel::Other;

        return match ($parentLevel) {
            RegionLevel::Country => RegionLevel::Province->value,
            RegionLevel::Province => RegionLevel::City->value,
            RegionLevel::City => RegionLevel::District->value,
            RegionLevel::District, RegionLevel::Street => RegionLevel::Street->value,
            RegionLevel::Other => RegionLevel::Other->value,
        };
    }

    private function resolveDepth(?DberpRegion $parent): int
    {
        if (! $parent instanceof DberpRegion) {
            return 1;
        }

        return $parent->depth + 1;
    }

    private function resolvePath(?DberpRegion $parent, string $regionCode): string
    {
        if (! $parent instanceof DberpRegion) {
            return $regionCode;
        }

        $base = $parent->path === null || $parent->path === '' ? (string) $parent->region_code : (string) $parent->path;

        return $base === '' ? $regionCode : $base.'/'.$regionCode;
    }

    private function resolveMergerName(?DberpRegion $parent, string $name): string
    {
        if (! $parent instanceof DberpRegion) {
            return $name;
        }

        $base = $parent->merger_name === null || $parent->merger_name === '' ? (string) $parent->name : (string) $parent->merger_name;

        return $base === '' ? $name : $base.'/'.$name;
    }
}
