<?php
namespace App\Http\Controllers\Api\V1\Admin;

use App\Domain\Transfers\TransferStateMachine;
use App\Enums\LedgerDirection;
use App\Enums\LedgerType;
use App\Enums\TransferStatus;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\ReasonRequest;
use App\Http\Resources\TransferResource;
use App\Models\Transfer;
use App\Services\AuditService;
use App\Services\LedgerService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class TransferAdminController extends Controller
{
    public function index(Request $request)
    {
        $query=Transfer::with('sendMerchant','receiveMerchant','corridor')
            ->when($request->status,fn($q,$value)=>$q->where('status',$value))
            ->when($request->merchant_id,fn($q,$value)=>$q->where(fn($x)=>$x->where('send_merchant_id',$value)->orWhere('receive_merchant_id',$value)))
            ->when($request->date_from,fn($q,$value)=>$q->whereDate('created_at','>=',$value))
            ->when($request->date_to,fn($q,$value)=>$q->whereDate('created_at','<=',$value))
            ->when($request->corridor_id,fn($q,$value)=>$q->where('corridor_id',$value))
            ->when($request->q,fn($q,$value)=>$q->where(fn($x)=>$x->where('reference','like',"%{$value}%")->orWhere('sender_name','like',"%{$value}%")->orWhere('recipient_name','like',"%{$value}%")))
            ->latest();

        return TransferResource::collection($query->paginate(min($request->integer('per_page',20),100)));
    }

    public function show(Transfer $transfer): TransferResource
    {
        return new TransferResource($transfer->load('events','sendMerchant','receiveMerchant'));
    }

    public function hold(ReasonRequest $request,Transfer $transfer,TransferStateMachine $states,AuditService $audit): TransferResource
    {
        $transfer=DB::transaction(function() use ($request,$transfer,$states,$audit) {
            $locked=Transfer::whereKey($transfer->id)->lockForUpdate()->firstOrFail();
            $old=$locked->getAttributes();
            $locked->forceFill(['hold_reason'=>$request->string('reason')->toString()])->save();
            $locked=$states->transition($locked,TransferStatus::OnHold,$request->user(),['reason'=>$request->string('reason')->toString()],$request->ip());
            $audit->record('transfer.held',$locked,$old,$locked->getAttributes());
            return $locked;
        },5);

        return new TransferResource($transfer);
    }

    public function release(ReasonRequest $request,Transfer $transfer,TransferStateMachine $states,AuditService $audit): TransferResource
    {
        $transfer=DB::transaction(function() use ($request,$transfer,$states,$audit) {
            $locked=Transfer::whereKey($transfer->id)->lockForUpdate()->firstOrFail();
            $old=$locked->getAttributes();
            $locked=$states->transition($locked,TransferStatus::Issued,$request->user(),['reason'=>$request->string('reason')->toString()],$request->ip());
            $locked->forceFill(['hold_reason'=>null])->save();
            $audit->record('transfer.released',$locked,$old,$locked->getAttributes());
            return $locked;
        },5);

        return new TransferResource($transfer);
    }

    public function cancel(
        ReasonRequest $request,
        Transfer $transfer,
        TransferStateMachine $states,
        LedgerService $ledger,
        AuditService $audit,
    ): TransferResource {
        if($request->string('reference_confirmation')->toString()!==$transfer->reference){
            throw \Illuminate\Validation\ValidationException::withMessages(['reference_confirmation'=>['The transfer reference confirmation does not match.']]);
        }

        $transfer=DB::transaction(function() use ($request,$transfer,$states,$ledger,$audit) {
            $locked=Transfer::whereKey($transfer->id)->lockForUpdate()->firstOrFail();
            $locked->loadMissing('sendMerchant');
            $old=$locked->getAttributes();
            $locked->forceFill(['cancel_reason'=>$request->string('reason')->toString()])->save();
            $locked=$states->transition($locked,TransferStatus::Cancelled,$request->user(),['reason'=>$request->string('reason')->toString()],$request->ip());
            $ledger->post($locked->sendMerchant,LedgerDirection::Debit,$locked->total_collected_minor,$locked->send_currency,LedgerType::Reversal,$request->user(),$locked,null,'Cancellation reversal '.$locked->reference);
            $audit->record('transfer.cancelled',$locked,$old,$locked->getAttributes());
            return $locked;
        },5);

        return new TransferResource($transfer);
    }
}
