<?php

namespace App\Models;

use App\Http\Helpers\ProfitCalculator;
use App\Http\Helpers\StripeClient;
use App\Notifications\PaymentVerified;
use App\Notifications\RefundCreated;
use App\Notifications\ServiceOrderNotification;
use Carbon\Carbon;
use Exception;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Stripe\Checkout\Session;

class Transaction extends Model
{
    use HasFactory;
    use SoftDeletes;

    protected $casts = [
        'confirmed_at' => 'datetime:Y-m-d',
    ];

    public static function boot()
    {
        parent::boot();


        self::created(function($model){
            $model->amount_received= $model->type==='Payment'?
                ProfitCalculator::calculate($model->amount): $model->amount;
            $model->save();
        });

    }

    protected $guarded =[];

    public function milestone(){
        return $this->belongsTo(Milestone::class)->withTrashed();
    }
    public function fromWallet(){
        return $this->belongsTo(Wallet::class, 'from','id','payments')->withTrashed();
    }
    public function toWallet(){
        return $this->belongsTo(Wallet::class, 'to','id','payouts')->withTrashed();
    }

    public function updateWalletsOnPayment(Session $intent){
        $milestone = Milestone::find($intent->metadata['milestone_id']);
        $amount = $milestone->amount;
        $userWallet = Wallet::find($intent->metadata['from']);
        $userWallet->reserveMoneyActivity($amount, $milestone->name);
        if($intent->metadata['use_wallet']=="true"){
             $userWallet->available_funds=0;
             $userWallet->save();
        }
        $mainWallet = Wallet::find(1);
        $mainWallet->reserveMoneyActivity($amount, $milestone->name);
        if ($intent->metadata['milestonable_type'] == "App\Models\Proposal") {
            $worker = Proposal::find($intent->metadata['milestonable_id'])
                ->proposalable;
            $worker->notify(new PaymentVerified($milestone));
            $worker->wallet->addUpcomingMoneyActivity(ProfitCalculator::calculate($amount) , $milestone->name);
            $contract= Proposal::find($intent->metadata['milestonable_id'])->project->contract;
            $contract->paid_amount += $milestone->amount;
            $contract->save();
        } else {
            $worker = ServiceOrder::find($intent->metadata['milestonable_id'])
                ->service->serviceable;
            $worker->wallet->addUpcomingMoneyActivity(ProfitCalculator::calculate($amount), $milestone->name);
            $worker->notify(new ServiceOrderNotification(ServiceOrder::find($intent->metadata['milestonable_id'])));
            $worker->notify(new PaymentVerified($milestone));
        }
        if($milestone->start_date == null || Carbon::parse($milestone->start_date)->addDay() < now()){
            $milestone->start_date = now();
            $milestone->due_date = now()->addWeekdays($milestone->duration);
        }
        if($milestone->due_date == null){
            $milestone->due_date = now()->addWeekdays($milestone->duration);
        }
        $milestone->save();
    }

    public function refund(?bool $fromAdmin=false,?bool $toWallet=true){
        try {
            $transaction = DB::transaction(function () use ($fromAdmin,$toWallet) {
                $this->status = 'refunded';
                $userActivity= $this->fromWallet->activities()->create([
                    'amount' => $this->amount,
                    'activity_en' =>   $toWallet? $this->amount .' has been released to your available balance due to refund' : $this->amount .  ' has been refunded from your muhandisin wallet to source' ,
                    'activity_ar' =>   $toWallet? $this->amount . ' تم ردهم الى رصيدك المتاح ' :$this->amount . ' تم ردهم الي حسابك من محفظة مهندسين  ',
                    'status' => 'refunded',
                ]);
                if(!$toWallet){
                    $mainWalletActivity =$this->toWallet->activities()->create([
                        'amount' => $this->amount,
                        'activity_en' => $this->amount .' has been refunded from your muhandisin wallet to source ',
                        'activity_ar' => $this->amount .' تم ردهم الي المصدر من محفظة مهندسين ',
                        'status' => 'refunded',
                    ]);
                }

                $this->fromWallet->reserved_funds -= $this->amount;
                if($toWallet){
                    $this->fromWallet->available_funds += $this->amount;
                }
                $this->fromWallet->upcoming_payments -= $this->amount;
                $this->fromWallet->owner->notify(new RefundCreated(
                    $this->milestone
                ));
                $this->save();
                $this->fromWallet->save();
                $refund= $this->milestone->refund($fromAdmin);
                if(!$toWallet){
                    $this->toWallet->available_funds -= $this->amount*0.04;
                    $this->toWallet->reserved_funds -=$this->amount;
                    $this->toWallet->upcoming_payments -= $this->amount;
                    $this->toWallet->save();
                    $refund= StripeClient::stripe()->refunds->create([
                        'payment_intent'=>json_decode($this->stripe_data)->payment_intent
                    ]);
                }
               return $refund;
            });
            return is_null($transaction)
                ? back(303)->with(["success" => "success"])
                : $transaction;
        } catch(\Stripe\Exception\CardException|\Stripe\Exception\ApiErrorException| \Stripe\Exception\InvalidRequestException $e) {
            StripeError::create([
                'user_id'=>Auth::user()->id,
                'error_message'=>$e->getMessage(),
                'error_code'=>$e->getJsonBody()['error']['request_log_url'],
                'stripe_url'=>$e->getStripeCode(),
            ]);
            throw $e;
        }   catch (Exception $e) {
            throw $e;
        }
    }
}
