<?php

namespace App\Services;

use App\Models\Catalog\MainCategory;
use App\Models\Catalog\Product;
use App\Models\Catalog\SubCategory;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

/**
 * Deterministic product SKU generation.
 *
 * Format: {MAIN_CAT}-{SUBCAT}-{SEQ:3}  e.g. GNJ-CFFC-001, JMC-GEN-012.
 * Requires $sub to have its mainCategory relation loaded.
 *
 * A main category with no subcategories yet is valid — products may be
 * created against it directly. Those fall back to a {MAIN_CAT}-GEN-{SEQ:3}
 * sequence scoped to that main category (sub_category_id null).
 */
class CategorySkuService
{
    public function next(?SubCategory $sub, ?MainCategory $main = null): string
    {
        return $sub !== null
            ? $this->nextForSubCategory($sub)
            : $this->nextForMainCategory($main);
    }

    private function nextForSubCategory(SubCategory $sub): string
    {
        $prefix = $this->prefixFor($sub);

        return DB::transaction(function () use ($sub, $prefix) {
            DB::table('sub_categories')->where('id', $sub->id)->lockForUpdate()->first();

            return $this->nextWithPrefix($prefix, fn ($q) => $q->where('sub_category_id', $sub->id));
        });
    }

    private function nextForMainCategory(?MainCategory $main): string
    {
        $prefix = $this->mainPrefixFor($main).'-GEN';

        return DB::transaction(function () use ($main, $prefix) {
            if ($main) {
                DB::table('main_categories')->where('id', $main->id)->lockForUpdate()->first();
            }

            return $this->nextWithPrefix($prefix, fn ($q) => $q->where('main_category_id', $main?->id)->whereNull('sub_category_id'));
        });
    }

    /** @param  callable(Builder): void  $scope */
    private function nextWithPrefix(string $prefix, callable $scope): string
    {
        $query = DB::table('products')->where('sku', 'like', $prefix.'-%');
        $scope($query);

        $existing = $query->orderByDesc('sku')->value('sku');

        $nextSeq = 1;
        if ($existing && preg_match('/-(\d+)$/', $existing, $m)) {
            $nextSeq = (int) $m[1] + 1;
        }

        return $prefix.'-'.str_pad((string) $nextSeq, 3, '0', STR_PAD_LEFT);
    }

    private function prefixFor(SubCategory $sub): string
    {
        $mainCode = Str::upper(trim((string) ($sub->mainCategory?->sort_code ?? '')));

        $subCode = Str::upper(trim(preg_replace('/\s+/', '', (string) ($sub->sort_code ?? ''))));
        if ($subCode === '') {
            $subCode = substr(preg_replace('/[^A-Z]/i', '', Str::upper($sub->name)), 0, 5) ?: 'PROD';
        }

        return $mainCode !== '' ? "{$mainCode}-{$subCode}" : $subCode;
    }

    private function mainPrefixFor(?MainCategory $main): string
    {
        $code = Str::upper(trim((string) ($main?->sort_code ?? '')));
        if ($code === '' && $main !== null) {
            $code = substr(preg_replace('/[^A-Z]/i', '', Str::upper($main->name)), 0, 5);
        }

        return $code !== '' ? $code : 'PROD';
    }
}
