<?php

namespace App\Services\Accounting;

use Illuminate\Support\Facades\DB;
use Carbon\Carbon;

class JournalNumberService
{
    /**
     * Get the next number in the sequence for a specific hotel and prefix
     * 
     * @param int $hotelId
     * @param string $prefix
     * @param string|Carbon|null $date
     * @param int $padding
     * @return string
     */
    public function getNextNumber($hotelId, $prefix, $date = null, $padding = 6)
    {
        $date = $date ? Carbon::parse($date) : now();
        $yearMonth = $date->format('Ym');

        return DB::transaction(function () use ($hotelId, $prefix, $yearMonth, $padding) {
            /** @var \stdClass|null $sequence */
            $sequence = DB::table('journal_sequences')
                ->where('hotel_id', $hotelId)
                ->where('prefix', $prefix)
                ->where('year_month', $yearMonth)
                ->lockForUpdate()
                ->first();

            if (!$sequence) {
                $nextValue = 1;
                DB::table('journal_sequences')->insert([
                    'hotel_id' => $hotelId,
                    'prefix' => $prefix,
                    'year_month' => $yearMonth,
                    'last_value' => $nextValue,
                    'created_at' => now(),
                    'updated_at' => now(),
                ]);
            } else {
                $nextValue = $sequence->last_value + 1;
                DB::table('journal_sequences')
                    ->where('id', $sequence->id)
                    ->update([
                        'last_value' => $nextValue,
                        'updated_at' => now(),
                    ]);
            }

            return sprintf('%s-%s-%s', $prefix, $yearMonth, str_pad($nextValue, $padding, '0', STR_PAD_LEFT));
        });
    }

    /**
     * Get next number without date (Option C: {prefix}-{seq})
     */
    public function getNextSequential($hotelId, $prefix, $padding = 6)
    {
        return DB::transaction(function () use ($hotelId, $prefix, $padding) {
            /** @var \stdClass|null $sequence */
            $sequence = DB::table('journal_sequences')
                ->where('hotel_id', $hotelId)
                ->where('prefix', $prefix)
                ->whereNull('year_month')
                ->lockForUpdate()
                ->first();

            if (!$sequence) {
                $nextValue = 1;
                DB::table('journal_sequences')->insert([
                    'hotel_id' => $hotelId,
                    'prefix' => $prefix,
                    'year_month' => null,
                    'last_value' => $nextValue,
                    'created_at' => now(),
                    'updated_at' => now(),
                ]);
            } else {
                $nextValue = $sequence->last_value + 1;
                DB::table('journal_sequences')
                    ->where('id', $sequence->id)
                    ->update([
                        'last_value' => $nextValue,
                        'updated_at' => now(),
                    ]);
            }

            return sprintf('%s-%s', $prefix, str_pad($nextValue, $padding, '0', STR_PAD_LEFT));
        });
    }

    /**
     * Reset sequence for a hotel and prefix
     */
    public function resetSequence($hotelId, $prefix, $yearMonth = null)
    {
        return DB::table('journal_sequences')
            ->where('hotel_id', $hotelId)
            ->where('prefix', $prefix)
            ->where('year_month', $yearMonth)
            ->update(['last_value' => 0, 'updated_at' => now()]);
    }
}
