<?php

namespace Modules\Bms\Services\Adapters\Native;

use Illuminate\Support\Facades\DB;
use Modules\Bms\Entities\BmsAsset;
use Modules\Bms\Entities\BmsAssetMovement;
use Modules\Bms\Services\Contracts\AssetServiceContract;

/**
 * BMS-native asset register (Release 2). Owns bms_assets + bms_asset_movements
 * and keeps BMS portable: the ERP's AssetManagement module is registered but
 * not present in this codebase. If it is later confirmed live, swap the binding
 * in Config/config.php to an ErpAdapter — no caller changes. Flowers and other
 * consumables stay in ERP inventory and go through StockServiceContract.
 */
class NativeAssetService implements AssetServiceContract
{
    /**
     * Register a trackable asset and log its opening movement.
     *
     * @param  array  $attrs  name, [code], [name_ar], [category], [location_id],
     *                         [hall_id], [condition], [status], [quantity],
     *                         [unit_value], [serial_no], [purchased_at], [notes],
     *                         [moved_by]
     */
    public function register(int $businessId, array $attrs): int
    {
        return DB::transaction(function () use ($businessId, $attrs) {
            $asset = BmsAsset::create([
                'business_id'  => $businessId,
                'code'         => $attrs['code'] ?? $this->generateCode($businessId),
                'name'         => $attrs['name'],
                'name_ar'      => $attrs['name_ar'] ?? null,
                'category'     => $attrs['category'] ?? 'other',
                'location_id'  => $attrs['location_id'] ?? null,
                'hall_id'      => $attrs['hall_id'] ?? null,
                'condition'    => $attrs['condition'] ?? 'good',
                'status'       => $attrs['status'] ?? 'available',
                'quantity'     => (int) ($attrs['quantity'] ?? 1),
                'unit_value'   => $attrs['unit_value'] ?? null,
                'serial_no'    => $attrs['serial_no'] ?? null,
                'purchased_at' => $attrs['purchased_at'] ?? null,
                'photo_path'   => $attrs['photo_path'] ?? null,
                'notes'        => $attrs['notes'] ?? null,
            ]);

            $this->logMovement($asset, [
                'to_location_id' => $asset->location_id,
                'to_hall_id'     => $asset->hall_id,
                'quantity'       => $asset->quantity,
                'condition'      => $asset->condition,
                'reason'         => 'register',
                'moved_by'       => $attrs['moved_by'] ?? null,
            ]);

            return (int) $asset->id;
        });
    }

    /**
     * Move an asset to a new location (warehouse/hall) with an audit trail.
     *
     * @param  array  $meta  [to_hall_id], [condition], [status], [quantity],
     *                       [transfer_id], [moved_by], [reason], [notes]
     */
    public function transferLocation(int $businessId, int $assetId, int $toLocationId, array $meta = []): void
    {
        DB::transaction(function () use ($businessId, $assetId, $toLocationId, $meta) {
            $asset = BmsAsset::where('business_id', $businessId)->lockForUpdate()->findOrFail($assetId);

            $fromLocationId = $asset->location_id;
            $fromHallId     = $asset->hall_id;
            $toHallId       = array_key_exists('to_hall_id', $meta) ? $meta['to_hall_id'] : null;

            $asset->update([
                'location_id' => $toLocationId,
                'hall_id'     => $toHallId,
                'condition'   => $meta['condition'] ?? $asset->condition,
                'status'      => $meta['status'] ?? 'available',
            ]);

            $this->logMovement($asset, [
                'from_location_id' => $fromLocationId,
                'from_hall_id'     => $fromHallId,
                'to_location_id'   => $toLocationId,
                'to_hall_id'       => $toHallId,
                'transfer_id'      => $meta['transfer_id'] ?? null,
                'quantity'         => $meta['quantity'] ?? $asset->quantity,
                'condition'        => $meta['condition'] ?? $asset->condition,
                'reason'           => $meta['reason'] ?? 'transfer',
                'moved_by'         => $meta['moved_by'] ?? null,
                'notes'            => $meta['notes'] ?? null,
            ]);
        });
    }

    /** Resolve an asset's current state. */
    public function find(int $businessId, int $assetId): ?array
    {
        $asset = BmsAsset::where('business_id', $businessId)->find($assetId);
        if (! $asset) {
            return null;
        }

        return [
            'id'          => $asset->id,
            'code'        => $asset->code,
            'name'        => $asset->name,
            'category'    => $asset->category,
            'location_id' => $asset->location_id,
            'hall_id'     => $asset->hall_id,
            'condition'   => $asset->condition,
            'status'      => $asset->status,
            'quantity'    => $asset->quantity,
        ];
    }

    /** Write a movement audit row. */
    protected function logMovement(BmsAsset $asset, array $attrs): BmsAssetMovement
    {
        return BmsAssetMovement::create(array_merge([
            'business_id' => $asset->business_id,
            'asset_id'    => $asset->id,
        ], $attrs));
    }

    /** Generate the next sequential asset tag (AST-000001) for a business. */
    protected function generateCode(int $businessId): string
    {
        $seq = (int) (BmsAsset::where('business_id', $businessId)->count()) + 1;
        do {
            $code = 'AST-' . str_pad((string) $seq, 6, '0', STR_PAD_LEFT);
            $exists = BmsAsset::where('business_id', $businessId)->where('code', $code)->exists();
            $seq++;
        } while ($exists);

        return $code;
    }
}
