<?php

namespace App\Services;

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

class OrderNumberService
{
    /**
     * Generate a unique checkout order number (CK- prefix)
     * Format: CK-{SELLER_CODE}-{SEQUENCE}
     * Example: CK-SIT-000001
     */
    public function generateCheckoutOrderNumber($user): string
    {
        $sellerCode = strtoupper(substr($user->name, 0, 3));
        $prefix = "CK-{$sellerCode}";

        $sequence = $this->getNextSequence($prefix);

        return $prefix . '-' . str_pad($sequence, 6, '0', STR_PAD_LEFT);
    }

    /**
     * Generate a unique sales page order number (SP- prefix)
     * Format: SP-{SELLER_CODE}-{SEQUENCE}
     * Example: SP-SIT-000001
     */
    public function generateSalesPageOrderNumber($user): string
    {
        $sellerCode = strtoupper(substr($user->name, 0, 3));
        $prefix = "SP-{$sellerCode}";

        $sequence = $this->getNextSequence($prefix);

        return $prefix . '-' . str_pad($sequence, 6, '0', STR_PAD_LEFT);
    }

    /**
     * Generate a unique webhook order number (WH- prefix)
     * Format: WH-{SEQUENCE}
     * Example: WH-000001
     */
    public function generateWebhookOrderNumber(): string
    {
        $prefix = "WH";

        $sequence = $this->getNextSequence($prefix);

        return $prefix . '-' . str_pad($sequence, 6, '0', STR_PAD_LEFT);
    }

    /**
     * Get next sequence number atomically
     * Uses database row locking to prevent race conditions
     *
     * @param string $prefix The order prefix (e.g., "CK-SIT", "SP-SIT", "WH")
     * @return int The next sequence number
     */
    protected function getNextSequence(string $prefix): int
    {
        return DB::transaction(function () use ($prefix) {
            // Lock the row for update to prevent race conditions
            $sequence = OrderSequence::where('prefix', $prefix)
                ->lockForUpdate()
                ->first();

            if (!$sequence) {
                // Create new sequence starting at 1
                $sequence = OrderSequence::create([
                    'prefix' => $prefix,
                    'current_value' => 1,
                ]);

                return 1;
            }

            // Increment and save
            $nextValue = $sequence->current_value + 1;
            $sequence->current_value = $nextValue;
            $sequence->save();

            return $nextValue;
        });
    }

    /**
     * Initialize sequences from existing orders
     * Run this once during deployment to set initial sequence values
     *
     * @param string $prefix The order prefix
     * @param int $startValue The starting sequence value
     */
    public function initializeSequence(string $prefix, int $startValue): void
    {
        OrderSequence::updateOrCreate(
            ['prefix' => $prefix],
            ['current_value' => $startValue]
        );
    }

    /**
     * Get current sequence value without incrementing
     *
     * @param string $prefix The order prefix
     * @return int Current sequence value
     */
    public function getCurrentSequence(string $prefix): int
    {
        $sequence = OrderSequence::where('prefix', $prefix)->first();

        return $sequence ? $sequence->current_value : 0;
    }

    /**
     * Reset sequence to a specific value
     * Use with caution - only for maintenance/migration
     *
     * @param string $prefix The order prefix
     * @param int $value The value to set
     */
    public function resetSequence(string $prefix, int $value): void
    {
        DB::transaction(function () use ($prefix, $value) {
            $sequence = OrderSequence::where('prefix', $prefix)
                ->lockForUpdate()
                ->first();

            if ($sequence) {
                $sequence->current_value = $value;
                $sequence->save();
            } else {
                OrderSequence::create([
                    'prefix' => $prefix,
                    'current_value' => $value,
                ]);
            }
        });
    }
}
