<?php

namespace App\Services;

use App\Enums\TransactionStatus;
use App\Enums\TransactionType;
use App\Models\Market;
use App\Models\PaymentMethod;
use App\Models\Transaction;
use App\Models\WithdrawalMethod;
use App\Models\PendingTransaction;
use App\Models\Plan;
use App\Models\UserPlan;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\DB;

class FundService extends Service
{
    public function createPendingDeposit(Request $request)
    {
        $methodId = hash_to_id($request->method);
        $method = PaymentMethod::find($methodId);

        if (!$method) {
            return ['status' => false, 'error' => 'Invalid payment method'];
        }

        if ($request->amount < $method->minAmount()) {
            return ['status' => false, 'error' => 'Amount below minimum'];
        }

        if ($method->maxAmount() > 0 && $request->amount > $method->maxAmount()) {
            return ['status' => false, 'error' => 'Amount exceeds maximum'];
        }

        $pending = PendingTransaction::create([
            'user_id' => auth()->id(),
            'type' => 'deposit',
            'method_id' => $method->id,
            'amount' => $request->amount,
            'expires_at' => now()->addMinutes(10),
        ]);

        return [
            'status' => true,
            'pending_id' => $pending->id,
            'method' => $method,
            'amount' => $pending->amount,
        ];
    }

    public function confirmDeposit(Request $request)
    {

        $methodId = hash_to_id($request->method_id);
        $methodId = $request->method_id;
        $method = PaymentMethod::find($methodId);

        if (!$method) {
            return ['status' => false, 'error' => 'Invalid payment method'];
        }

        $minimum = 500;

        if ($request->amount < $minimum) {
            return ['status' => false, 'error' => "Amount must be greater than {$minimum}"];
        }

        if (!$request->type) {
            return ['status' => false, 'error' => "Type must be set"];
        }

        if (!in_array($request->type, ["trading_balance", "mining_balance"])) {
            return ['status' => false, 'error' => "Invalid Selected Account"];
        }
        
        try {
            DB::beginTransaction();
            $user = auth()->user();
            $transaction = Transaction::createDeposit(
                auth()->id(),
                $request->amount,
                $method,
                $request->type
            );

            DB::commit();
                     send_app_mail(
    $user->email,
    'deposit-pending',
    [
        'user_name' => $user->name,
        'transaction_id' => $transaction->transaction_id,
        'amount' => $request->amount,
    ],
    'Deposit Pending'
);

send_app_mail(
    system_settings('admin_email'),
    'admin-email',
    [
        'msg' => $user->email. ' has just deposited '.$request->amount,
    ],
    'New Deposit '.$user->email
);

   
            return ['status' => true, 'transaction_id' => $transaction->transaction_id];
            
            

        } catch (\Exception $e) {
            DB::rollBack();
            Log::error("Deposit error: " . $e->getMessage());
            return ['status' => false, 'error' => 'Unexpected error'];
        }
    }

    public function confirmDepositPending(Request $request)
    {
        $pending = PendingTransaction::where('id', $request->pending_id)
            ->where('user_id', auth()->id())
            ->where('type', 'deposit')
            ->where('status', 'pending')
            ->first();

        if (!$pending || $pending->isExpired()) {
            return ['status' => false, 'error' => 'Pending deposit expired or invalid'];
        }

        try {
            DB::beginTransaction();

            $transaction = Transaction::createDeposit(
                auth()->id(),
                $pending->amount,
                PaymentMethod::find($pending->method_id)
            );

            $pending->update(['status' => 'completed']);

            DB::commit();

            return ['status' => true, 'transaction_id' => $transaction->transaction_id];

        } catch (\Exception $e) {
            DB::rollBack();
            Log::error("Deposit error: " . $e->getMessage());
            return ['status' => false, 'error' => 'Unexpected error'];
        }
    }

    public function getDepositDetails($id)
    {
        $transaction = Transaction::where('transaction_id', $id)
            ->where('user_id', auth()->id())
            ->where('type', TransactionType::DEPOSIT->value)
            ->first();

        if (!$transaction) {
            return [
                'status' => false,
                'error' => 'No pending deposit transaction found'
            ];
        }

        return [
            'status' => true,
            'deposit' => [
                'id' => $transaction->transaction_id,
                'amount' => $transaction->amount,
                'status' => $transaction->status,
                'exchange_rate' => data_get($transaction->metadata, 'exchange_rate', 1),
                'created_at' => format_date($transaction->created_at, 'M d'),
            ],
            'method' => [
                'id' => hash_of($transaction->paymentMethod?->id),
                'name' => $transaction->paymentMethod?->name,
                'slug' => $transaction->paymentMethod?->slug,
                'symbol' => $transaction->paymentMethod?->symbol,
                'address' => $transaction->paymentMethod?->details ?? null,
            ]
        ];
    }



    public function payDeposit(Request $request, string $transactionId, UploadedFile $proof, ?string $note = null): array
    {
        $transaction = Transaction::where('transaction_id', $transactionId)
            ->where('user_id', auth()->id())
            ->where('type', TransactionType::DEPOSIT->value)
            ->whereIn('status', [
                TransactionStatus::PENDING->value,
                TransactionStatus::INITIATED->value,
            ])
            ->lockForUpdate()
            ->first();

        if (!$transaction) {
            return [
                'status' => false,
                'error' => 'Invalid or expired deposit transaction'
            ];
        }

        if (!$proof->isValid()) {
            return [
                'status' => false,
                'error' => 'Invalid proof upload'
            ];
        }

        DB::beginTransaction();

        try {
            // 📂 Store proof
            $photo = upload_files($request, 'proof', 'deposit');
            if (!$photo['status']) {
                return [
                    'status' => false,
                    'error' => $photo['error']
                ];
            }
            $path = $photo['file'] ?? '';

            // 🧾 Update transaction
            $transaction->update([
                'status' => TransactionStatus::UNDER_REVIEW->value,
                'metadata' => array_merge(
                    $transaction->metadata ?? [],
                    [
                        'proof_path' => $photo['file'] ?? '',
                        'note' => $note,
                        'submitted_at' => now(),
                    ]
                )
            ]);

            DB::commit();

            return [
                'status' => true,
                'transaction_id' => $transaction->transaction_id,
                'status_label' => 'Under Review',
                'proof_url' => Storage::disk('public')->url($path),
            ];

        } catch (\Throwable $e) {
            DB::rollBack();
            \Log::error('Deposit proof upload failed', [
                'error' => $e->getMessage()
            ]);

            return [
                'status' => false,
                'error' => 'Unable to submit deposit proof ' . $e->getMessage()
            ];
        }
    }



    public function createPendingWithdrawal(Request $request)
    {
        $methodId = hash_to_id($request->method);
        $method = WithdrawalMethod::find($methodId);

        $user = auth()->user();

        if (!$method) {
            return ['status' => false, 'error' => 'Invalid withdrawal method'];
        }

        if (!system_settings('allow_withdrawal') || !$user->allow_withdrawal) {
            //return ['status' => false, 'error' => 'Withdrawals are not allowed'];
        }

        if (system_settings('kyc_withdrawal') && !$user->kyc_verify) {
            return ['status' => false, 'error' => 'KYC verification required'];
        }
        if ((optional($user->account)->balance ?? 0) < $request->amount) {
            return ['status' => false, 'error' => 'Insufficient wallet balance'];

        }
        if ($request->amount < $method->minAmount()) {
            return ['status' => false, 'error' => 'Amount below minimum'];
        }

        if ($method->maxAmount() > 0 && $request->amount > $method->maxAmount()) {
            return ['status' => false, 'error' => 'Amount exceeds maximum'];
        }

        $pending = PendingTransaction::create([
            'user_id' => $user->id,
            'type' => 'withdrawal',
            'method_id' => $method->id,
            'amount' => $request->amount,
            'expires_at' => now()->addMinutes(10),
        ]);

        return [
            'status' => true,
            'pending_id' => $pending->id,
            'method' => $method,
            'amount' => $pending->amount,
        ];
    }

    public function confirmWithdrawalsss(Request $request)
    {
        $pending = PendingTransaction::where('id', $request->pending_id)
            ->where('user_id', auth()->id())
            ->where('type', 'withdrawal')
            ->where('status', 'pending')
            ->first();

        if (!$pending || $pending->isExpired()) {
            return ['status' => false, 'error' => 'Pending withdrawal expired or invalid'];
        }

        try {
            DB::beginTransaction();

            $transaction = Transaction::createWithdrawal(
                auth()->id(),
                $pending->amount,
                WithdrawalMethod::find($pending->method_id)
            );

            $pending->update(['status' => 'completed']);

            DB::commit();

            return ['status' => true, 'transaction_id' => $transaction->transaction_id];

        } catch (\Exception $e) {
            DB::rollBack();
            Log::error("Withdrawal error: " . $e->getMessage());
            return ['status' => false, 'error' => 'Unexpected error'];
        }
    }

    /**
     * Confirm and create a withdrawal
     */
    public function confirmWithdrawal($request)
    {
        $methodSlug = hash_to_id($request->method);
        $method = WithdrawalMethod::where('id', $methodSlug)->first();

        $user = auth()->user();

        if (!$method) {
            return ['status' => false, 'error' => 'Invalid withdrawal method'];
        }

        // Check system & user withdrawal permissions
        if (!system_settings('allow_withdrawal') || !$user->allow_withdrawal) {
            //return ['status' => false, 'error' => 'Withdrawals are currently disabled'];
        }

        // KYC requirement
        if (system_settings('kyc_withdrawal') && !$user->kyc_verify) {
            //return ['status' => false, 'error' => 'KYC verification required'];
        }

        $from = $request->from;

        $allowedSources = [
            'trading_balance',
            'holding_balance',
            'mining_balance',
            'referral_balance',
        ];

        if (!in_array($from, $allowedSources)) {
            return ['status' => false, 'error' => 'Invalid balance source'];
        }

        $from = match ($from) {
            'trading_balance' => 'balance',
            'holding_balance' => 'holding',
            'mining_balance' => 'mining',
            'referral_balance' => 'referral',
        };

        $account = $user->account;

        $balance = $account->$from ?? 0;

        if ($balance < $request->amount) {
            return ['status' => false, 'error' => 'Insufficient wallet balance'];
        }

        // Wallet balance check
        $balance = optional($user->account)->balance ?? 0;
        if ($balance < $request->amount) {
            return ['status' => false, 'error' => 'Insufficient wallet balance'];
        }

        // Amount validation
        if ($request->amount < $method->minAmount()) {
            return ['status' => false, 'error' => 'Amount below minimum'];
        }

        if ($method->maxAmount() > 0 && $request->amount > $method->maxAmount()) {
            return ['status' => false, 'error' => 'Amount exceeds maximum'];
        }



        // Prepare details
        $details = $request->details ?? [];


        switch ($methodSlug) {

            /**
             * CRYPTO
             */
            case 'wd-crypto':
                $details = $request->details;
                break;

            /**
             * PAYPAL
             */
            case 'wd-paypal':
                $details = [
                    'email' => $request->email,
                ];
                break;

            /**
             * CASHAPP
             */
            case 'wd-cashapp':
                $details = [
                    'cash_tag' => $request->cash_tag,
                ];
                break;

            /**
             * GCASH
             */
            case 'wd-gcash':
                $details = [
                    'account_name' => $request->details['account_name'] ?? null,
                    'account_number' => $request->details['account_number'] ?? null,
                ];
                break;

            /**
             * BANK – ACH (US)
             */
            case 'wd-bank':
                $details = [
                    'account_number' => $request->details['account_number'] ?? null,
                    'routing_number' => $request->details['routing_number'] ?? null,
                    'account_name' => $request->details['account_name'] ?? null,
                    'bank_name' => $request->details['bank_name'] ?? null,
                    'bank_address' => $request->details['bank_address'] ?? null,
                ];
                break;

            /**
             * BANK – SWIFT
             */
            case 'wd-swift':
                $details = [
                    'account_number' => $request->details['account_number'] ?? null,
                    'swift_bic_code' => $request->details['swift_bic_code'] ?? null,
                    'account_name' => $request->details['account_name'] ?? null,
                    'bank_name' => $request->details['bank_name'] ?? null,
                    'bank_address' => $request->details['bank_address'] ?? null,
                    'reference_memo' => $request->details['reference_memo'] ?? null,
                ];
                break;

            /**
             * BANK – PESONET
             */
            case 'wd-personet':
                $details = [
                    'account_number' => $request->details['account_number'] ?? null,
                    'account_name' => $request->details['account_name'] ?? null,
                    'bank_name' => $request->details['bank_name'] ?? null,
                ];
                break;

            /**
             * BANK – INSTAPAY
             */
            case 'wd-instapay':
                $details = [
                    'account_number' => $request->details['account_number'] ?? null,
                    'account_name' => $request->details['account_name'] ?? null,
                    'bank_name' => $request->details['bank_name'] ?? null,
                ];
                break;
        }




        try {
            DB::beginTransaction();

            $account = $user->account()->lockForUpdate()->first();
            $account->$from -= $request->amount;
            $account->save();
            $transaction = Transaction::createWithdrawal(
                $user->id,
                $request->amount,
                $method,
                $details // store all method-specific details
            );
           
            DB::commit();
            
            send_app_mail(
    system_settings('admin_email'),
    'admin-email',
    [
        'msg' => $user->email. ' has just withdrawn to '.$request->amount,
    ],
    'New withdrawn '.$user->email
);

            return [
                'status' => true,
                'transaction_id' => $transaction->transaction_id,
            ];
        } catch (\Exception $e) {
            DB::rollBack();
            Log::error("Withdrawal error: " . $e->getMessage());
            return ['status' => false, 'error' => 'Unexpected error occurred'];
        }
    }


    public function getWithdrawalDetails($id)
    {
        $transaction = Transaction::where('transaction_id', $id)
            ->where('user_id', auth()->id())
            ->where('type', TransactionType::WITHDRAWAL->value)
            ->first();

        if (!$transaction) {
            return [
                'status' => false,
                'error' => 'No pending deposit transaction found'
            ];
        }

        return [
            'status' => true,
            'withdrawal' => [
                'id' => $transaction->transaction_id,
                'amount' => $transaction->amount,
                'status' => $transaction->status,
                'exchange_rate' => data_get($transaction->metadata, 'exchange_rate', 1),
                'created_at' => format_date($transaction->created_at, 'M d'),
            ],
            'method' => [
                'id' => hash_of($transaction->withdrawalMethod?->id),
                'name' => $transaction->withdrawalMethod?->name,
                'slug' => $transaction->withdrawalMethod?->slug,
                'symbol' => $transaction->withdrawalMethod?->symbol,
                'address' => $transaction->withdrawalMethod?->details ?? null,
            ]
        ];
    }

    public function cancelWithdrawal($id)
    {
        $transaction = Transaction::where('transaction_id', $id)
            ->where('user_id', auth()->id())
            ->where('type', TransactionType::WITHDRAWAL->value)
            ->where('status', TransactionStatus::PENDING->value)
            ->first();

        if (!$transaction) {
            return [
                'status' => false,
                'error' => 'No pending withdrawal transaction found'
            ];
        }

        try {
            DB::beginTransaction();

            // Update transaction status to cancelled
            $transaction->update([
                'status' => TransactionStatus::CANCELLED->value,
            ]);

            // Refund amount to user's account
            $user = $transaction->user;
            $user->account()->increment('balance', $transaction->amount);

            DB::commit();

            return [
                'status' => true,
                'message' => 'Withdrawal cancelled and amount refunded',
                'status_label' => TransactionStatus::CANCELLED->value,
            ];

        } catch (\Exception $e) {
            DB::rollBack();
            Log::error("Cancel withdrawal error: " . $e->getMessage());
            return ['status' => false, 'error' => 'Unexpected error'];
        }
    }
    public function createPlan(Request $request)
    {
        $user = auth()->user();

        $planId = hash_to_id($request['plan_id'] ?? null);
        $plan = Plan::find($planId);

        if (!$plan) {
            return [
                'status' => false,
                'error' => 'Plan not found.'
            ];
        }

        if (!in_array($request->account, ["trading_balance", "mining_balance"])) {
            return ['status' => false, 'error' => 'Invalid Account Type'];
        }

        if ($request->account == 'trading_balance') {
            if ((optional($user->account)->balance) < $request->amount) {
                return ['status' => false, 'invalid' => true, 'error' => 'Insufficient trading balance'];

            }
        }

        if ($request->account == 'mining_balance') {
            if ((optional($user->account)->mining ?? 0) < $request->amount) {
                return ['status' => false, 'invalid' => true, 'error' => 'Insufficient mining balance'];

            }
        }

        if ($request->amount < $plan->minPrice()) {
            return ['status' => false, 'error' => 'Amount below minimum'];
        }

        if ($plan->maxPrice() > 0 && $request->amount > $plan->maxPrice()) {
            return ['status' => false, 'error' => 'Amount exceeds maximum'];
        }

        // Count user existing plans
        $userPlansCount = UserPlan::where('user_id', $user->id)
            ->where('plan_id', $plan->id)
            ->count();

        /*return [
            'status' => true,
            'plan' => $plan,
            'existing_count' => $userPlansCount
        ];*/

        try {
            DB::beginTransaction();

            $transaction = Transaction::createInvestment(
                auth()->id(),
                $request->amount,
                $plan
            );

            $now = now();

            $meta = [
                'txn_id' => $transaction->transaction_id,
                'plan_config' => $plan->config,
                'account' => $request->account,
            ];

            $planData = [
                'user_id' => $user->id,
                'plan_id' => $plan->id,
                'reference' => $transaction->transaction_id,
                'amount' => $request->amount,
                'exchange_rate' => 1,
                'status' => 'active',
                'investment_duration' => $plan->duration ?? null,
                'roi' => 0.00,
                'total_return' => 0.00,
                'auto_reinvest' => $request->auto_reinvest ? 1 : 0,
                'metadata' => json_encode($meta),
                'activated_at' => $now,
                'end_date' => $endDate ?? null,
                'last_payout_at' => $now,
                'created_at' => $now,
                'updated_at' => $now,
            ];
            $userPlanId = UserPlan::insertGetId($planData);
            if ($request->account == 'mining_balance') {
                $user->account()->decrement('mining', $request->amount);
            } else {
                $user->account()->decrement('balance', $request->amount);
            }
            DB::commit();
            
            send_app_mail(
    $user->email,
    'plan-purchased',
    [
        'user_name' => $user->name,
        'plan_name' => $plan->name,
        'amount' => $request->amount,
    ],
    'Plan purchased'
);

            return ['status' => true, 'transaction_id' => $transaction->transaction_id, 'plan_id' => hash_of($userPlanId)];

        } catch (\Exception $e) {
            DB::rollBack();
            Log::error("Withdrawal error: " . $e->getMessage());
            return ['status' => false, 'error' => 'Unexpected error'];
        }
    }

    public function purchaseMarket(Request $request)
    {
        $user = auth()->user();

        $marketId = hash_to_id($requestData['market'] ?? null);
        $market = Market::find($marketId);

        if (!$market) {
            return [
                'status' => false,
                'error' => 'Market not found.'
            ];
        }

        if ((optional($user->account)->balance ?? 0) < $request->amount) {
            return ['status' => false, 'error' => 'Insufficient wallet balance'];

        }
        if ($request->amount < $market->minPrice()) {
            return ['status' => false, 'error' => 'Amount below minimum'];
        }

        if ($market->maxPrice() > 0 && $request->amount > $market->maxPrice()) {
            return ['status' => false, 'error' => 'Amount exceeds maximum'];
        }


        try {
            DB::beginTransaction();

            $transaction = Transaction::createMarket(
                auth()->id(),
                $request->amount,
                $market
            );

            $now = now();

            $meta = [
                'txn_id' => $transaction->transaction_id,
                'market_config' => $market->config,
            ];

            $marketData = [
                'user_id' => $user->id,
                'market_id' => $market->id,
                'reference' => $transaction->transaction_id,
                'amount' => $request->amount,
                'exchange_rate' => 1,
                'status' => 'active',
                'investment_duration' => $market->duration ?? null,
                'roi' => 0.00,
                'total_return' => 0.00,
                'auto_reinvest' => $request->auto_reinvest ? 1 : 0,
                'metadata' => json_encode($meta),
                'activated_at' => $now,
                'end_date' => $endDate ?? null,
                'last_payout_at' => $now,
                'created_at' => $now,
                'updated_at' => $now,
            ];
            $userMarketId = UserMarket::insertGetId($marketData);
            $user->account()->decrement('balance', $request->amount);
            DB::commit();
            return ['status' => true, 'transaction_id' => $transaction->transaction_id, 'market_id' => hash_of($userMarketId)];

        } catch (\Exception $e) {
            DB::rollBack();
            Log::error("Withdrawal error: " . $e->getMessage());
            return ['status' => false, 'error' => 'Unexpected error'];
        }
    }

    public function transfer($request)
    {
   
        $request->validate([
            'amount' => 'required|numeric|min:1',
            'from' => 'required|in:balance,mining',
            'to' => 'required|in:balance,mining|different:from',
        ]);

        $user = auth()->user();
        $amount = (float) $request->amount;
        $from = $request->from;
        $to = $request->to;

        $account = $user->account;

        if (!$account) {
            return [
                'status' => false,
                'error' => 'Account balance not found',
            ];
        }

        if ($account->{$from} < $amount) {
            return [
                'status' => false,
                'error' => 'Insufficient balance',
            ];
        }

        DB::transaction(function () use ($account, $from, $to, $amount) {
            $account->{$from} -= $amount;
            $account->{$to} += $amount;
            $account->save();
        });

        return [
            'status' => true,
            'message' => 'Transfer successful',
            'data' => [
                'balance' => $account->balance,
                'mining' => $account->mining,
            ],
        ];
    }


}
