<?php

namespace App\Models;

use App\Http\Helpers\FileUploader;
use App\Http\Helpers\ProfitCalculator;
use App\Http\Helpers\StripeClient;
use App\Notifications\DeliverableAdded;
use App\Notifications\MilestoneAccepted;
use App\Models\Transaction;
use App\Notifications\PaymentVerified;
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;

class Milestone extends Model
{
    use HasFactory;
    use SoftDeletes;

    protected $guarded = [];
    /**
     * The attributes that should be cast.
     *
     * @var array
     */
    protected $casts = ['accepted_at' => 'datetime:Y-m-d', 'due_date' => 'datetime:Y-m-d',];

    protected $with = ["transaction",];

    public function deliverables()
    {
        return $this->hasMany(Deliverable::class);
    }

    public function milestonable()
    {
        return $this->morphTo()->withTrashed();
    }

    public function addFiles(array $files)
    {
        foreach ($files as $file) {
            $this->files()->create(['url' => FileUploader::store('deliverables/', $file), 'type' => FileUploader::getFileType($file),]);

        }
        $this->milestonable->getModel() instanceof Proposal ? $this->milestonable->project->user->notify(new DeliverableAdded($this)) : $this->milestonable->user->notify(new DeliverableAdded($this));
        return $this;
    }

    public function files()
    {
        return $this->hasMany(DeliverableFile::class);
    }

    public function accept()
    {
        if (Auth::user()->wallet->id != $this->transaction->from) {
            session()->flash('error', "You are not authorized to see this");
            return redirect(url()->previous());
        }
        try {
            $transaction = DB::transaction(function () {
                $this->update(["accepted_at" => now(),]);

                $worker = $this->milestonable->getModel() instanceof Proposal ? $this->milestonable->proposalable : $this->milestonable->service->serviceable;

                $this->transaction()->update(['status' => 'paid', 'confirmed_at' => now(), 'confirmed_by' => Auth::user()->id, 'to' => $worker->wallet->id,]);
                $net = $this->transaction->amount * .96;
                if($this->transaction->stripe_type != 'wallet_transaction'){
                    $intent = StripeClient::stripe()->paymentIntents->retrieve(json_decode($this->transaction->stripe_data)->payment_intent);
                    try {
                        if ($intent->latest_charge != null && StripeClient::stripe()->balanceTransactions->retrieve(StripeClient::stripe()->charges?->retrieve($intent->latest_charge)?->balance_transaction) != null) {
                            $net = StripeClient::stripe()->balanceTransactions->retrieve(StripeClient::stripe()->charges?->retrieve($intent->latest_charge)?->balance_transaction)->net / 100;
                        }
                    } catch (Exception $exception) {
                    }
                }
                $this->transaction->save();
                // update user wallet resrved funds and total amount
                $user_wallet = Wallet::find($this->transaction->from);
                $user_wallet->releaseMoneyActivity($this->transaction->amount, $this->name);
                $user_wallet->releaseUpcomingMoneyActivity($this->transaction->amount);
                Wallet::find(1)->releaseUpcomingMoneyActivity($this->transaction->amount);
                // update muhandisin's reserved funds and upcoming payments
                $amount = ProfitCalculator::calculate($this->transaction->amount);
                Wallet::find(1)->addUpcomingMoneyActivity(($this->transaction->amount + $amount), $this->name);
                $mainWallet = Wallet::find(1);

                $mainWallet->available_funds += ($net - $amount);
                $mainWallet->save();
                // update worker wallet reserved funds and upcoming payments
                $worker->wallet->releaseUpcomingMoneyActivity($amount);
                $worker->wallet->reserveMoneyActivity($amount, $this->name, false);
                // send email that milestone accepted and money is available
                $worker->notify(new MilestoneAccepted($this));
                $done = true;
                foreach ($this->milestonable->milestones as $milestone) {
                    if ($milestone->accepted_at == null) {
                        $done = false;
                        break;
                    }
                }
                if ($done) {
                    if ($this->milestonable instanceof ServiceOrder) {
                        $this->milestonable->update(["status" => "closed",]);
                        $this->milestonable->save();
                    } else {
                        $this->milestonable->project->update(["status" => "Completed",]);
                        $this->milestonable->project->save();
                        $this->milestonable->project->contract->update(["status" => "Closed",]);
                    }
                }
                $this->save();
                return $this;
            });
            return is_null($transaction) ? $this : $transaction;
        } catch (Exception $exception) {
            return redirect()->back()->withErrors(["error" => $exception->getMessage()])->with(["error" => $exception->getMessage()]);
        }
    }

    public function transaction()
    {
        return $this->hasOne(Transaction::class);
    }

    public function pay($clientSignature = null,?bool $useWallet=false)
    {

        $metaData = [
            'milestone_id' => $this->id,
            "milestonable_id" => $this->milestonable_id,
            "milestonable_type" => $this->milestonable_type,
            "from" => Auth::user()->wallet->id,
            "to" => 1,
            "worker_id" => $this->milestonable instanceof ServiceOrder ?
                $this->milestonable->service->serviceable_id :
                $this->milestonable->proposalable_id,
            "worker_type" => $this->milestonable instanceof ServiceOrder ?
                $this->milestonable->service->serviceable_type :
                $this->milestonable->proposalable_type,
            "due_date" => $this->due_date,
            "muh_client_signature" => $clientSignature,
            'use_wallet' => $useWallet,
        ];
        if($this->amount<= Auth::user()->wallet->available_funds && $useWallet){



            $transaction = [
                'amount' => $this->amount,
                'type' => 'Payment',
                'from' => $metaData['from'],
                "to" => $metaData['to'],
                'status' => 'reserved',
                'milestone_id' => $metaData['milestone_id'],
                'stripe_type' => 'wallet_transaction',
            ];
            $this->status = 'paid';
            $this->save();
            if($clientSignature!=null){
                if(get_class($this->milestonable)  == 'App\Models\Proposal'){
                    $this->milestonable->confirmPayment([
                        'client_signature' => $metaData['muh_client_signature'],
                        'paid_amount'=> $this->amount,
                    ]);
                }else{
                    $this->milestonable->client_signature = $metaData['muh_client_signature'];
                    $this->milestonable->save();
                }

            }

            $transaction = Transaction::create($transaction);
            $userWallet = Wallet::find($metaData['from']);
            $userWallet->reserveMoneyActivity($this->amount, $this->name);
            $userWallet->available_funds-=$this->amount;
            $userWallet->save();
            $mainWallet = Wallet::find(1);
            $mainWallet->reserveMoneyActivity($this->amount, $this->name);

            if ($metaData['milestonable_type'] == "App\Models\Proposal") {

                $worker = Proposal::find($metaData['milestonable_id'])
                    ->proposalable;
                $worker->notify(new PaymentVerified($this));
                $worker->wallet->addUpcomingMoneyActivity(ProfitCalculator::calculate($this->amount) , $this->name);
            } else {
                $worker = ServiceOrder::find($metaData['milestonable_id'])
                    ->service->serviceable;
                    $worker->wallet->addUpcomingMoneyActivity(ProfitCalculator::calculate($this->amount), $this->name);
                    $worker->notify(new ServiceOrderNotification(ServiceOrder::find($metaData['milestonable_id'])));
                $worker->notify(new PaymentVerified($this));
            }
            if($this->start_date == null || Carbon::parse($this->start_date)->addDay() < now()){
                $this->start_date = now();
                $this->due_date = now()->addWeekdays($this->duration);
            }
            if($this->due_date == null){
                $this->due_date = now()->addWeekdays($this->duration);
            }

            $this->save();
            return false;
        }
        // if($clientSignature != null){
        //     $metaData['client_signature'] = $clientSignature;
        // }
        // dd($metaData);
        else{
            $amount = $this->amount;
            if ($useWallet){
                $amount-= Auth::user()->wallet->available_funds;
            }
            $intent = StripeClient::createPaymentSession([
                "metadata" => $metaData,
                'line_items' =>
                    [
                        ['price_data' => [
                            'currency' => 'usd',
                            'product_data' => [
                                'name' => 'Payment for ' . $this->name . ' milestone',
                            ],
                            'unit_amount' => ceil($amount)  * 100,],
                            'quantity' => 1,
                        ],
                    ],
                "invoice_creation" => ["enabled" => true,],
                "customer_email" => $this->milestonable instanceof ServiceOrder ?
                    $this->milestonable->user->email :
                    $this->milestonable->project->user->email,
            ]);
            return $intent;
        }
    }

    public function refund(?bool $fromAdmin = false)
    {
        $this->milestonable->refund($fromAdmin);
        $this->status = 'refunded';
        $this->save();
        return $this;
    }
}
