<?php

namespace App\Services;

use App\Models\NumberingRule;
use Illuminate\Support\Facades\DB;

class NumberingService extends Service
{
    /**
     * 채번 규칙 기반 번호 생성
     *
     * @param  string  $documentType  문서유형 (quote, order, sale, work_order, material_receipt)
     * @param  array  $params  외부 파라미터 (pair_code, product_category 등)
     * @return string|null 생성된 번호 (규칙 없으면 null → 호출자가 기존 로직 사용)
     */
    public function generate(string $documentType, array $params = []): ?string
    {
        $tenantId = $this->tenantId();

        $rule = NumberingRule::withoutGlobalScopes()
            ->where('tenant_id', $tenantId)
            ->where('document_type', $documentType)
            ->where('is_active', true)
            ->first();

        if (! $rule) {
            return null;
        }

        $segments = $rule->pattern;
        $result = '';
        $scopeKey = '';

        foreach ($segments as $segment) {
            switch ($segment['type']) {
                case 'static':
                    $result .= $segment['value'];
                    break;

                case 'separator':
                    $result .= $segment['value'];
                    break;

                case 'date':
                    $result .= now()->format($segment['format']);
                    break;

                case 'param':
                    $value = $params[$segment['key']] ?? $segment['default'] ?? '';
                    $result .= $value;
                    $scopeKey = $value;
                    break;

                case 'mapping':
                    $inputValue = $params[$segment['key']] ?? '';
                    $value = $segment['map'][$inputValue] ?? $segment['default'] ?? '';
                    $result .= $value;
                    $scopeKey = $value;
                    break;

                case 'sequence':
                    $periodKey = match ($rule->reset_period) {
                        'daily' => now()->format('ymd'),
                        'monthly' => now()->format('Ym'),
                        'yearly' => now()->format('Y'),
                        'never' => 'all',
                        default => now()->format('ymd'),
                    };

                    $nextSeq = $this->nextSequence(
                        $tenantId,
                        $documentType,
                        $scopeKey,
                        $periodKey
                    );

                    $result .= str_pad(
                        (string) $nextSeq,
                        $rule->sequence_padding,
                        '0',
                        STR_PAD_LEFT
                    );
                    break;
            }
        }

        return $result;
    }

    /**
     * 미리보기 (시퀀스 증가 없이 다음 번호 예측)
     */
    public function preview(string $documentType, array $params = []): ?array
    {
        $tenantId = $this->tenantId();

        $rule = NumberingRule::withoutGlobalScopes()
            ->where('tenant_id', $tenantId)
            ->where('document_type', $documentType)
            ->where('is_active', true)
            ->first();

        if (! $rule) {
            return null;
        }

        $segments = $rule->pattern;
        $result = '';
        $scopeKey = '';

        foreach ($segments as $segment) {
            switch ($segment['type']) {
                case 'static':
                case 'separator':
                    $result .= $segment['value'];
                    break;
                case 'date':
                    $result .= now()->format($segment['format']);
                    break;
                case 'param':
                    $value = $params[$segment['key']] ?? $segment['default'] ?? '';
                    $result .= $value;
                    $scopeKey = $value;
                    break;
                case 'mapping':
                    $inputValue = $params[$segment['key']] ?? '';
                    $value = $segment['map'][$inputValue] ?? $segment['default'] ?? '';
                    $result .= $value;
                    $scopeKey = $value;
                    break;
                case 'sequence':
                    $periodKey = match ($rule->reset_period) {
                        'daily' => now()->format('ymd'),
                        'monthly' => now()->format('Ym'),
                        'yearly' => now()->format('Y'),
                        'never' => 'all',
                        default => now()->format('ymd'),
                    };

                    $currentSeq = (int) DB::table('numbering_sequences')
                        ->where('tenant_id', $tenantId)
                        ->where('document_type', $documentType)
                        ->where('scope_key', $scopeKey)
                        ->where('period_key', $periodKey)
                        ->value('last_sequence');

                    $result .= str_pad(
                        (string) ($currentSeq + 1),
                        $rule->sequence_padding,
                        '0',
                        STR_PAD_LEFT
                    );
                    break;
            }
        }

        return [
            'preview_number' => $result,
            'document_type' => $documentType,
            'rule_name' => $rule->rule_name,
        ];
    }

    /**
     * Atomic sequence increment (MySQL UPSERT)
     */
    private function nextSequence(
        int $tenantId,
        string $documentType,
        string $scopeKey,
        string $periodKey
    ): int {
        DB::statement(
            'INSERT INTO numbering_sequences
                (tenant_id, document_type, scope_key, period_key, last_sequence, created_at, updated_at)
             VALUES (?, ?, ?, ?, 1, NOW(), NOW())
             ON DUPLICATE KEY UPDATE
                last_sequence = last_sequence + 1,
                updated_at = NOW()',
            [$tenantId, $documentType, $scopeKey, $periodKey]
        );

        return (int) DB::table('numbering_sequences')
            ->where('tenant_id', $tenantId)
            ->where('document_type', $documentType)
            ->where('scope_key', $scopeKey)
            ->where('period_key', $periodKey)
            ->value('last_sequence');
    }
}