<?php

declare(strict_types=1);

namespace App\Business\MasterData;

use App\Models\DberpRegion;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

/**
 * 为手工维护地区生成全局唯一的 {@see DberpRegion::$region_code}。
 *
 * 有上级时：{country_code}-CUSTOM-{parent_segment}-{sequence}
 * - parent_segment：优先为上级 {@code region_code} 的安全化片段；若无法得到非空片段则退化为 {@code id{上级主键}}。
 * 顶级（无上级）：{country_code}-CUSTOM-ROOT-{sequence}
 *
 * sequence：从 1 递增的十进制整数，在同一前缀下唯一。
 */
final class DberpCustomRegionCodeService
{
    public function allocateNext(?DberpRegion $parent, ?string $rootCountryCode = null): string
    {
        if (! $parent instanceof DberpRegion) {
            $country = (string) ($rootCountryCode ?? '');

            if ($country === '') {
                $country = 'CN';
            }

            return $this->allocateWithPrefix($country.'-CUSTOM-ROOT-');
        }

        $country = (string) $parent->country_code;
        $segment = $this->parentSegment($parent);
        $prefix = $country.'-CUSTOM-'.$segment.'-';

        return $this->allocateWithPrefix($prefix);
    }

    private function allocateWithPrefix(string $prefix): string
    {
        return DB::transaction(function () use ($prefix): string {
            $existing = DberpRegion::query()
                ->where('region_code', 'like', $prefix.'%')
                ->lockForUpdate()
                ->pluck('region_code')
                ->all();

            $pattern = '/^'.preg_quote($prefix, '/').'(\d+)$/';
            $max = 0;

            foreach ($existing as $code) {
                if (preg_match($pattern, (string) $code, $matches) === 1) {
                    $max = max($max, (int) $matches[1]);
                }
            }

            $next = $max + 1;
            $candidate = $prefix.$next;

            while (DberpRegion::query()->where('region_code', $candidate)->exists()) {
                $next++;
                $candidate = $prefix.$next;
            }

            return $candidate;
        });
    }

    private function parentSegment(DberpRegion $parent): string
    {
        $raw = (string) $parent->region_code;
        $sanitized = (string) preg_replace('/[^A-Za-z0-9-]+/', '-', $raw);
        $sanitized = trim((string) preg_replace('/-+/', '-', $sanitized), '-');

        if ($sanitized === '' || Str::length($sanitized) > 80) {
            return 'id'.$parent->getKey();
        }

        return $sanitized;
    }
}
