<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Carbon\Carbon;

class Order extends Model
{
    use SoftDeletes;

    protected $fillable = [
        'user_id',
        'currency_id',
        'type',
        'side',
        'open_price',
        'close_price',
        'amount',
        'margin',
        'volume',
        'status',
        'open_time',
        'close_time',
        'expiry_time',
        'take_profit',
        'stop_loss',
        'is_trailing_stop',
        'trailing_stop_distance',
        'executed_price',
        'executed_amount',
        'remaining_amount',
        'fee',
        'fee_currency'
    ];

    protected $casts = [
        'open_price' => 'decimal:8',
        'close_price' => 'decimal:8',
        'amount' => 'decimal:8',
        'margin' => 'decimal:8',
        'volume' => 'decimal:8',
        'take_profit' => 'decimal:8',
        'stop_loss' => 'decimal:8',
        'trailing_stop_distance' => 'decimal:8',
        'executed_price' => 'decimal:8',
        'executed_amount' => 'decimal:8',
        'remaining_amount' => 'decimal:8',
        'fee' => 'decimal:8',
        'is_trailing_stop' => 'boolean',
        'open_time' => 'datetime',
        'close_time' => 'datetime',
        'expiry_time' => 'datetime'
    ];

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

        static::creating(function ($order) {
            if ($order->status === 'open' && !$order->open_time) {
                $order->open_time = Carbon::now();
            }
        });
    }

    public function user()
    {
        return $this->belongsTo(User::class);
    }

    public function currency()
    {
        return $this->belongsTo(Currency::class);
    }

    public function scopeOpen($query)
    {
        return $query->where('status', 'open');
    }

    public function scopePending($query)
    {
        return $query->where('status', 'pending');
    }

    public function scopeClosed($query)
    {
        return $query->where('status', 'closed');
    }

    public function scopeBuy($query)
    {
        return $query->where('side', 'buy');
    }

    public function scopeSell($query)
    {
        return $query->where('side', 'sell');
    }

    public function scopeMarket($query)
    {
        return $query->where('type', 'market');
    }

    public function scopeLimit($query)
    {
        return $query->where('type', 'pending');
    }

    public function close()
    {
        if ($this->status !== 'open') {
            return false;
        }

        $this->update([
            'status' => 'closed',
            'close_price' => $this->side === 'buy' ? $this->currency->bid : $this->currency->ask,
            'close_time' => Carbon::now(),
        ]);

        $profitLoss = $this->calculateProfitLoss();
        
        $transaction = Transaction::create([
            'user_id' => $this->user_id,
            'order_id' => $this->id,
            'type' => $this->shouldLiquidate() ? 'margin_liquidation' : 'close_position',
            'amount' => $profitLoss,
            'confirmed' => 1
        ]);

        return true;
    }

    /**
     * Calculate profit or loss for the order.
     *
     * @param bool $includeFees Whether to include fees in the calculation
     * @param bool $asPercentage Whether to return the result as a percentage
     * @return float
     */
    public function calculateProfitLoss($includeFees = false, $asPercentage = false)
    {
        if ($this->close_price === null || $this->open_price === null || $this->volume === null) {
            return 0;
        }

        // Calculate base P/L
        $basePL = ($this->close_price - $this->open_price) * $this->volume;
        
        $sharePerLot = 100;
        
        if ($this->currency->type->code == 'currencies') {
            if (str_contains($this->currency->code, 'JPY')) {
                $basePL = $basePL * 1000;
            } else {
                $basePL = $basePL * 100000;
            }
        } else if ($this->currency->type->code == 'stocks') {
            $basePL = $basePL * $sharePerLot;
        }
        
        // Adjust for buy/sell direction
        $pl = $this->side === 'buy' ? $basePL : -$basePL;

        // Include fees if requested
        if ($includeFees && $this->fee !== null) {
            $pl -= $this->fee;
        }

        // Convert to percentage if requested
        if ($asPercentage) {
            $initialValue = $this->open_price * $this->volume;
            return $initialValue != 0 ? ($pl / $initialValue) * 100 : 0;
        }

        return $pl;
    }

    /**
     * Calculate unrealized profit/loss for open orders
     *
     * @param bool $includeFees Whether to include fees in the calculation
     * @param bool $asPercentage Whether to return the result as a percentage
     * @return float
     */
    public function calculateUnrealizedPL($includeFees = false, $asPercentage = false)
    {
        if ($this->status !== 'open' || $this->open_price === null || $this->volume === null) {
            return 0;
        }

        $currentPrice = $this->side === 'buy' ? $this->currency->bid : $this->currency->ask;
        
        // Calculate base P/L
        $basePL = ($currentPrice - $this->open_price) * $this->volume;

        $sharePerLot = 100;
        
        if ($this->currency->type->code == 'currencies') {
            if (str_contains($this->currency->code, 'JPY')) {
                $basePL = $basePL * 1000;
            } else {
                $basePL = $basePL * 100000;
            }
        } else if ($this->currency->type->code == 'stocks') {
            $basePL = $basePL * $sharePerLot;
        }
        
        // Adjust for buy/sell direction
        $pl = $this->side === 'buy' ? $basePL : -$basePL;

        // Include fees if requested
        if ($includeFees && $this->fee !== null) {
            $pl -= $this->fee;
        }

        // Convert to percentage if requested
        if ($asPercentage) {
            $initialValue = $this->open_price * $this->volume;
            return $initialValue != 0 ? ($pl / $initialValue) * 100 : 0;
        }

        return $pl;
    }

    /**
     * Get the current profit/loss status
     *
     * @return string 'profit', 'loss', or 'break-even'
     */
    public function getPLStatus()
    {
        if ($this->status === 'open') {
            $pl = $this->calculateUnrealizedPL();
        } else {
            $pl = $this->calculateProfitLoss();
        }

        if ($pl > 0) {
            return 'text-success';
        } elseif ($pl < 0) {
            return 'text-danger';
        } else {
            return 'text-secondary';
        }
    }

    /**
     * Get the profit/loss amount with proper formatting
     *
     * @param bool $includeFees Whether to include fees in the calculation
     * @param bool $asPercentage Whether to return the result as a percentage
     * @return string
     */
    public function getFormattedPL($includeFees = false, $asPercentage = false)
    {
        if ($this->status === 'open') {
            $pl = $this->calculateUnrealizedPL($includeFees, $asPercentage);
        } else {
            $pl = $this->calculateProfitLoss($includeFees, $asPercentage);
        }

        if ($asPercentage) {
            return number_format($pl, 2) . '%';
        }

        return number_format($pl, 2);
    }

    /**
     * Check if the order has reached its take profit level
     *
     * @return bool
     */
    public function hasReachedTakeProfit()
    {
        if (!$this->take_profit || $this->status !== 'open') {
            return false;
        }

        $currentPrice = $this->side === 'buy' ? $this->currency->bid : $this->currency->ask;
        
        if ($this->side === 'buy') {
            return $currentPrice >= $this->take_profit;
        } else {
            return $currentPrice <= $this->take_profit;
        }
    }

    /**
     * Check if the order has reached its stop loss level
     *
     * @return bool
     */
    public function hasReachedStopLoss()
    {
        if (!$this->stop_loss || $this->status !== 'open') {
            return false;
        }

        $currentPrice = $this->side === 'buy' ? $this->currency->bid : $this->currency->ask;
        
        if ($this->side === 'buy') {
            return $currentPrice <= $this->stop_loss;
        } else {
            return $currentPrice >= $this->stop_loss;
        }
    }

    public function getFormattedVolumeAttribute()
    {
        return $this->volume ? number_format($this->volume, 2) : null;
    }

    public function getFormattedOpenPriceAttribute()
    {
        $decimals = 2;
        
        if ($this->currency->type->code == 'currencies') {
            $decimals = 5;
        }

        if (str_contains($this->currency->code, 'JPY') && $this->currency->type->code == 'currencies') {
            $decimals = 3;
        }
        return $this->open_price ? number_format($this->open_price, $decimals) : null;
    }

    public function getFormattedClosePriceAttribute()
    {
        $decimals = 2;
        
        if ($this->currency->type->code == 'currencies') {
            $decimals = 5;
        }

        if (str_contains($this->currency->code, 'JPY') && $this->currency->type->code == 'currencies') {
            $decimals = 3;
        }
        return $this->close_price ? number_format($this->close_price, $decimals) : null;
    }

    public function getFormattedAmountAttribute()
    {
        return $this->amount ? number_format($this->amount, 2) : null;
    }   

    public function getFormattedTakeProfitAttribute()
    {
        return $this->take_profit ? number_format($this->take_profit, 2) : null;
    }   

    public function getFormattedStopLossAttribute()
    {
        return $this->stop_loss ? number_format($this->stop_loss, 2) : null;
    }

    public function getFormattedFeeAttribute()
    {
        return $this->fee ? number_format($this->fee, 2) : null;
    }

    /**
     * Check if the order should be liquidated due to margin issues
     *
     * @return bool
     */
    public function shouldLiquidate()
    {
        if ($this->status !== 'open') {
            return false;
        }

        $user = $this->user;
        $marginLevel = $user->calculateMarginLevel();
        
        // Liquidation threshold (100%)
        if ($marginLevel <= 100) {
            return true;
        }

        // Check if unrealized loss exceeds available margin
        $unrealizedPL = $this->calculateUnrealizedPL(true);
        $freeMargin = $user->calculateFreeMargin();
        
        if ($unrealizedPL < 0 && abs($unrealizedPL) > $freeMargin) {
            return true;
        }

        return false;
    }
}