<?php

namespace App\Http\Controllers\Accounting;

use App\Http\Controllers\Controller;
use App\Models\Invoice;
use App\Models\Customer;
use App\Models\SalesPerson;
use App\Models\FinancialTransaction;
use App\Models\Setting;
use App\Models\FinancialTreasury;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use App\Services\WaSenderService;
use App\Services\FinancialTransactionService;
use App\Services\LoyaltyService;
use App\Services\WhmcsService;

class InvoiceController extends Controller
{
    protected $waSender;
    protected $transactionService;
    protected $loyaltyService;

    public function __construct(
        WaSenderService $waSender,
        \App\Services\FinancialTransactionService $transactionService,
        \App\Services\LoyaltyService $loyaltyService
    ) {
        $this->waSender = $waSender;
        $this->transactionService = $transactionService;
        $this->loyaltyService = $loyaltyService;
    }



    public function print(Invoice $invoice)
    {
        $invoice->load(['items', 'customer']);
        $settings = Setting::getGroup('general');
        return view('accounting.invoices.print', compact('invoice', 'settings'));
    }

    public function sendWhatsapp(Invoice $invoice)
    {
        $invoice->load('customer');
        $phone = $invoice->customer->whatsapp ?? $invoice->customer->phone;

        if (!$phone) {
            return back()->with('error', 'Customer has no phone number defined.');
        }

        $result = $this->waSender->sendWhatsAppTemplate($phone, 'client_invoice_new', [
            'customer_name' => $invoice->customer->name,
            'invoice_code' => $invoice->code,
            'total' => number_format((float) $invoice->total, 2),
            'currency' => 'EGP',
            'due_date' => $invoice->due_date ? $invoice->due_date->format('Y-m-d') : 'Immediate',
            'invoice_link' => route('accounting.invoices.print', $invoice)
        ]);

        if ($result['success']) {
            return back()->with('success', 'WhatsApp message sent successfully.');
        } else {
            return back()->with('error', 'Failed to send WhatsApp: ' . $result['message']);
        }
    }

    public function index(Request $request)
    {
        $query = Invoice::with('customer', 'salesPerson');

        if ($request->filled('status')) {
            $query->where('status', $request->status);
        }

        if ($request->filled('is_tax_invoice')) {
            $query->where('is_tax_invoice', $request->is_tax_invoice);
        }

        // Stats
        $stats = [
            'total_count' => Invoice::count(),
            'total_value' => (float) Invoice::sum('total'),
            'paid_value' => (float) Invoice::sum('paid_amount'),
            'pending_value' => (float) Invoice::where('status', '!=', 'cancelled')->sum(\DB::raw('total - paid_amount')),
            'due_today_count' => Invoice::where('due_date', '=', date('Y-m-d'))->where('status', '!=', 'paid')->count(),
            'due_today_value' => (float) Invoice::where('due_date', '=', date('Y-m-d'))->where('status', '!=', 'paid')->sum(\DB::raw('total - paid_amount')),
        ];

        // Status counts for filter tabs
        $statusCounts = [
            'all' => Invoice::count(),
            'unpaid' => Invoice::where('status', 'unpaid')->count(),
            'partial' => Invoice::where('status', 'partial')->count(),
            'paid' => Invoice::where('status', 'paid')->count(),
            'cancelled' => Invoice::where('status', 'cancelled')->count(),
            'draft' => Invoice::where('status', 'draft')->count(),
        ];

        $invoices = $query->latest()->paginate(15)->withQueryString();
        return view('accounting.invoices.index', compact('invoices', 'stats', 'statusCounts'));
    }


    public function create()
    {
        $customers = Customer::orderBy('name')->get();
        $projects = \App\Models\Project::orderBy('name')->get();
        $salesPeople = SalesPerson::where('is_active', true)->get();
        $currencies = \App\Models\Currency::where('is_active', true)->get();
        $defaultCurrency = $currencies->where('is_base', true)->first() ?? $currencies->first();

        // Generate code
        $lastInvoice = Invoice::latest()->first();
        $nextId = $lastInvoice ? ($lastInvoice->id + 1) : 1;
        $code = 'INV-' . date('Y') . '-' . str_pad($nextId, 4, '0', STR_PAD_LEFT);

        $products = \App\Models\Product::where('is_active', true)->get();
        $units = \App\Models\Unit::where('is_active', true)->get();
        $treasuries = \App\Models\FinancialTreasury::where('is_active', true)->get();

        return view('accounting.invoices.create', compact('customers', 'projects', 'salesPeople', 'currencies', 'defaultCurrency', 'code', 'products', 'units', 'treasuries'));
    }

    public function getCustomerData(Customer $customer)
    {
        return response()->json([
            'balance' => (float) $customer->balance,
            'loyalty_points' => (float) $customer->loyalty_points,
            'loyalty_enabled' => $customer->loyalty_enabled,
        ]);
    }

    use \App\Traits\HasLockCheck;

    public function store(Request $request)
    {
        $rules = [
            'customer_id' => 'required|exists:customers,id',
            'currency_id' => 'nullable|exists:currencies,id',
            'exchange_rate' => 'nullable|numeric|min:0',
            'project_id' => 'nullable|exists:projects,id',
            'code' => 'required|unique:invoices,code',
            'date' => 'required|date',
            'due_date' => 'nullable|date|after_or_equal:date',
            'items' => 'required|array|min:1',
            'items.*.description' => 'required|string',
            'items.*.quantity' => 'required|numeric|min:0.01',
            'items.*.unit_price' => 'required|numeric|min:0',
            'items.*.serial_key' => 'nullable|string',
            'items.*.unit_id' => 'nullable|exists:units,id',
            'items.*.start_date' => 'nullable|date',
            'items.*.end_date' => 'nullable|date|after_or_equal:items.*.start_date',
            'tax_percent' => 'numeric|min:0',
            'discount_type' => 'nullable|in:fixed,percent',
            'discount_value' => 'nullable|numeric|min:0',
            'shipping_amount' => 'nullable|numeric|min:0',
            'extra_expenses_amount' => 'nullable|numeric|min:0',
            'extra_expenses_note' => 'nullable|string',
            'notes' => 'nullable|string',
            'sales_person_id' => 'nullable|exists:sales_people,id',
            'commission_amount' => 'nullable|numeric|min:0',
            // Payment Fields
            'amount' => 'nullable|numeric|min:0',
            'treasury_id' => 'nullable|exists:financial_treasuries,id',
            'wallet_amount' => 'nullable|numeric|min:0',
            'redeem_points' => 'nullable|numeric|min:0',
            'payment_exchange_rate' => 'nullable|numeric|min:0',
        ];

        $validated = $request->validate($rules);

        if ($this->isDateLocked($validated['date'])) {
            return back()->with('error', 'The selected date is locked for accounting.')->withInput();
        }

        $currency_id = $validated['currency_id'] ?? null;
        $exchange_rate = $validated['exchange_rate'] ?? 1.0;

        // If no currency selected, use base
        if (!$currency_id) {
            $base = \App\Helpers\CurrencyHelper::getBaseCurrency();
            $currency_id = $base ? $base->id : null;
            $exchange_rate = 1.0;
        }

        // Calculate totals
        $subtotal = 0;
        foreach ($validated['items'] as $item) {
            $subtotal += ($item['quantity'] * $item['unit_price']);
        }

        $taxAmount = ($subtotal * ($validated['tax_percent'] ?? 14)) / 100;

        // Calculate Discount
        $discountType = $validated['discount_type'] ?? 'fixed';
        $discountValue = $validated['discount_value'] ?? 0;
        $discountAmount = 0;

        if ($discountType === 'percent') {
            $discountAmount = ($subtotal * $discountValue) / 100;
        } else {
            $discountAmount = $discountValue;
        }

        $shippingAmount = $validated['shipping_amount'] ?? 0;
        $extraExpensesAmount = $validated['extra_expenses_amount'] ?? 0;

        $total = $subtotal + $taxAmount - $discountAmount + $shippingAmount + $extraExpensesAmount;

        $invoice = Invoice::create([
            'customer_id' => $validated['customer_id'],
            'currency_id' => $currency_id,
            'exchange_rate' => $exchange_rate,
            'project_id' => $validated['project_id'] ?? null,
            'code' => $validated['code'],
            'date' => $validated['date'],
            'due_date' => $validated['due_date'],
            'subtotal' => $subtotal,
            'tax_percent' => $validated['tax_percent'] ?? 14,
            'tax_amount' => $taxAmount,
            'discount_type' => $discountType,
            'discount_value' => $discountValue,
            'discount_amount' => $discountAmount,
            'shipping_amount' => $shippingAmount,
            'extra_expenses_amount' => $extraExpensesAmount,
            'extra_expenses_note' => $validated['extra_expenses_note'] ?? null,
            'total' => $total,
            'status' => 'unpaid',
            'notes' => $validated['notes'],
            'uuid' => Str::uuid(),
            'sales_person_id' => $validated['sales_person_id'] ?? null,
            'commission_amount' => $validated['commission_amount'] ?? 0,
        ]);

        // Create Sales Commission record if applicable
        if ($invoice->sales_person_id && $invoice->commission_amount > 0) {
            \App\Models\SalesCommission::create([
                'invoice_id' => $invoice->id,
                'sales_person_id' => $invoice->sales_person_id,
                'amount' => $invoice->commission_amount,
                'status' => 'pending',
                'notes' => 'Commission for invoice #' . $invoice->code,
            ]);

            $this->notifySalesCommission($invoice);
        }

        foreach ($validated['items'] as $item) {
            $lineTotal = $item['quantity'] * $item['unit_price'];
            $invoiceItem = $invoice->items()->create([
                'product_id' => $item['product_id'] ?? null,
                'description' => $item['description'],
                'quantity' => $item['quantity'],
                'unit_price' => $item['unit_price'],
                'total' => $lineTotal,
                'serial_key' => $item['serial_key'] ?? null,
                'unit_id' => $item['unit_id'] ?? null,
                'start_date' => $item['start_date'] ?? null,
                'end_date' => $item['end_date'] ?? null,
            ]);

            // If a serial is provided, also add it to the Customer's software licenses
            if (!empty($item['serial_key'])) {
                \App\Models\SoftwareLicense::updateOrCreate(
                    [
                        'customer_id' => $invoice->customer_id,
                        'serial_key' => $item['serial_key'],
                    ],
                    [
                        'software_name' => $item['description'],
                        'start_date' => $invoice->date,
                        'status' => 'active',
                        'notes' => 'Generated from Invoice #' . $invoice->code,
                    ]
                );
            }
        }

        // Handle optional instant payment
        $this->handleInvoicePayments($invoice, [
            'amount' => $request->amount,
            'treasury_id' => $request->treasury_id,
            'wallet_amount' => $request->wallet_amount,
            'redeem_points' => $request->redeem_points,
            'exchange_rate' => $request->payment_exchange_rate ?? $exchange_rate,
            'date' => $validated['date'],
        ]);

        return redirect()->route('accounting.invoices.show', $invoice)
            ->with('success', 'Invoice created successfully.');
    }

    public function edit(Invoice $invoice)
    {
        $customers = Customer::active()->get();
        $products = \App\Models\Product::where('is_active', true)->get();
        $salesPeople = SalesPerson::where('is_active', true)->get();
        $units = \App\Models\Unit::where('is_active', true)->get();
        $invoice->load('items');

        return view('accounting.invoices.edit', compact('invoice', 'customers', 'products', 'salesPeople', 'units'));
    }

    public function update(Request $request, Invoice $invoice)
    {
        $validated = $request->validate([
            'customer_id' => 'required|exists:customers,id',
            'is_tax_invoice' => 'nullable|boolean',
            'date' => 'required|date',
            'due_date' => 'nullable|date|after_or_equal:date',
            'items' => 'required|array|min:1',
            'items.*.description' => 'required|string',
            'items.*.quantity' => 'required|numeric|min:0.01',
            'items.*.unit_price' => 'required|numeric|min:0',
            'items.*.serial_key' => 'nullable|string',
            'items.*.unit_id' => 'nullable|exists:units,id',
            'items.*.start_date' => 'nullable|date',
            'items.*.end_date' => 'nullable|date|after_or_equal:items.*.start_date',
            'tax_percent' => 'numeric|min:0',
            'discount_type' => 'nullable|in:fixed,percent',
            'discount_value' => 'nullable|numeric|min:0',
            'shipping_amount' => 'nullable|numeric|min:0',
            'extra_expenses_amount' => 'nullable|numeric|min:0',
            'extra_expenses_note' => 'nullable|string',
            'notes' => 'nullable|string',
            'sales_person_id' => 'nullable|exists:sales_people,id',
            'commission_amount' => 'nullable|numeric|min:0',
        ]);

        if ($this->isDateLocked($validated['date']) || $this->isDateLocked($invoice->date)) {
            return back()->with('error', 'The date is locked for accounting.');
        }

        // If payments exist, restrict some updates
        if ($invoice->paid_amount > 0) {
            $invoice->update([
                'date' => $validated['date'],
                'due_date' => $validated['due_date'],
                'notes' => $validated['notes'],
                'sales_person_id' => $validated['sales_person_id'],
                'commission_amount' => $validated['commission_amount'],
                // Technically extra expenses/shipping shouldn't change if paid? 
                // But user might want to adjust if they forgot. 
                // If partially paid, changing total is risky. 
                // Let's allow updating extra headers but warn user in UI (which we did).
                // Actually, changing total changes remaining balance.
                // If paid_amount > 0, we restricted item changes. 
                // We should probably restrict discount/expense changes too to avoid total mismatch if we don't recalc items? 
                // But items are restricted.
                // If we allow changing Discount/Expenses, we MUST recalc total.
                // Let's NOT allow changing financial values if paid > 0 for safety, as per Edit View readonly state.
            ]);
        } else {
            // Full update
            $subtotal = 0;
            foreach ($validated['items'] as $item) {
                $subtotal += ($item['quantity'] * $item['unit_price']);
            }
            $taxAmount = ($subtotal * ($validated['tax_percent'] ?? 14)) / 100;

            // Calculate Discount
            $discountType = $validated['discount_type'] ?? 'fixed';
            $discountValue = $validated['discount_value'] ?? 0;
            $discountAmount = 0;

            if ($discountType === 'percent') {
                $discountAmount = ($subtotal * $discountValue) / 100;
            } else {
                $discountAmount = $discountValue;
            }

            $shippingAmount = $validated['shipping_amount'] ?? 0;
            $extraExpensesAmount = $validated['extra_expenses_amount'] ?? 0;

            $total = $subtotal + $taxAmount - $discountAmount + $shippingAmount + $extraExpensesAmount;

            $invoice->update([
                'customer_id' => $validated['customer_id'],
                'date' => $validated['date'],
                'due_date' => $validated['due_date'],
                'subtotal' => $subtotal,
                'tax_percent' => $validated['tax_percent'] ?? 14,
                'tax_amount' => $taxAmount,
                'discount_type' => $discountType,
                'discount_value' => $discountValue,
                'discount_amount' => $discountAmount,
                'shipping_amount' => $shippingAmount,
                'extra_expenses_amount' => $extraExpensesAmount,
                'extra_expenses_note' => $validated['extra_expenses_note'] ?? null,
                'total' => $total,
                'notes' => $validated['notes'],
                'sales_person_id' => $validated['sales_person_id'],
                'commission_amount' => $validated['commission_amount'],
            ]);

            // Sync Items
            $invoice->items()->delete();
            foreach ($validated['items'] as $item) {
                //$invoice->items()->create($item + ['total' => $item['quantity'] * $item['unit_price']]);
                // Need to manually create to match fillable
                $lineTotal = $item['quantity'] * $item['unit_price'];
                $invoice->items()->create([
                    'description' => $item['description'],
                    'quantity' => $item['quantity'],
                    'unit_price' => $item['unit_price'],
                    'total' => $lineTotal,
                    'serial_key' => $item['serial_key'] ?? null,
                    'unit_id' => $item['unit_id'] ?? null,
                    'start_date' => $item['start_date'] ?? null,
                    'end_date' => $item['end_date'] ?? null,
                ]);
            }
        }

        // Update Sales Commission
        $commission = \App\Models\SalesCommission::where('invoice_id', $invoice->id)->first();
        if ($invoice->sales_person_id && $invoice->commission_amount > 0) {
            if ($commission) {
                $oldAmount = $commission->amount;
                $commission->update([
                    'sales_person_id' => $invoice->sales_person_id,
                    'amount' => $invoice->commission_amount,
                    'status' => $commission->status == 'cancelled' ? 'pending' : $commission->status
                ]);
            } else {
                \App\Models\SalesCommission::create([
                    'invoice_id' => $invoice->id,
                    'sales_person_id' => $invoice->sales_person_id,
                    'amount' => $invoice->commission_amount,
                    'status' => 'pending',
                ]);
            }
        } elseif ($commission) {
            $commission->delete();
        }

        return redirect()->route('accounting.invoices.show', $invoice)->with('success', 'Invoice updated successfully.');
    }

    protected function notifySalesCommission(Invoice $invoice)
    {
        $salesPerson = SalesPerson::find($invoice->sales_person_id);
        if (!$salesPerson)
            return;

        $phone = $salesPerson->whatsapp ?: $salesPerson->phone;
        if (!$phone)
            return;

        $message = "تم تسجيل عمولة جديدة لك!\n";
        $message .= "فاتورة رقم: #{$invoice->code}\n";
        $message .= "العميل: {$invoice->customer->name}\n";
        $message .= "قيمة العمولة: " . number_format($invoice->commission_amount, 2) . " ج.م\n";
        $message .= "الحالة: قيد الانتظار\n";
        $message .= "شكراً لك!";

        try {
            $this->waSender->sendMessage($phone, $message);
        } catch (\Exception $e) {
            // Log error
        }
    }

    public function show(Invoice $invoice)
    {
        $invoice->load(['items', 'customer', 'transactions.treasury']);
        $treasuries = FinancialTreasury::where('is_active', true)->get();
        return view('accounting.invoices.show', compact('invoice', 'treasuries'));
    }

    public function addPayment(Request $request, Invoice $invoice)
    {
        $validated = $request->validate([
            'amount' => 'nullable|numeric|min:0',
            'exchange_rate' => 'nullable|numeric|min:0',
            'treasury_id' => 'required_with:amount|exists:financial_treasuries,id',
            'wallet_amount' => 'nullable|numeric|min:0',
            'redeem_points' => 'nullable|numeric|min:0',
            'date' => 'required|date',
        ]);

        if ($this->isDateLocked($validated['date'])) {
            return back()->with('error', 'The payment date is locked for accounting.');
        }

        try {
            \DB::beginTransaction();
            $this->handleInvoicePayments($invoice, $validated);
            \DB::commit();
            return back()->with('success', 'Payment recorded successfully.');
        } catch (\Exception $e) {
            \DB::rollBack();
            return back()->with('error', 'Payment Failed: ' . $e->getMessage());
        }
    }

    private function handleInvoicePayments(Invoice $invoice, array $data)
    {
        // Amount entered is in INVOICE Currency
        $paymentAmountInvoiceCurrency = $data['amount'] ?? 0;
        $exchangeRate = $data['exchange_rate'] ?? 1.0;

        $walletAmount = $data['wallet_amount'] ?? 0;
        $pointsToRedeem = $data['redeem_points'] ?? 0;

        if ($paymentAmountInvoiceCurrency <= 0 && $walletAmount <= 0 && $pointsToRedeem <= 0) {
            return;
        }

        $remainingBefore = (float) $invoice->total - (float) $invoice->paid_amount;

        if ($walletAmount > $invoice->customer->balance) {
            throw new \Exception('Insufficient wallet balance.');
        }

        // 2. Handle Loyalty Redemption
        if ($pointsToRedeem > 0) {
            $this->loyaltyService->redeemPoints($invoice, $pointsToRedeem);
            $remainingBefore = (float) $invoice->total - (float) $invoice->paid_amount;
        }

        // 3. Handle Wallet Payment
        if ($walletAmount > 0) {
            if ($walletAmount > $remainingBefore + 0.01) {
                throw new \Exception('Wallet payment exceeds remaining invoice balance.');
            }

            $invoice->customer->walletTransactions()->create([
                'type' => 'invoice_payment',
                'amount' => $walletAmount,
                'date' => $data['date'],
                'notes' => "Payment for Invoice #{$invoice->code}",
                'user_id' => auth()->id(),
                'model_id' => $invoice->id,
                'model_type' => Invoice::class,
            ]);

            $invoice->increment('paid_amount', $walletAmount);
            $remainingBefore -= $walletAmount;
        }

        // 4. Handle Cash/Bank Payment
        if ($paymentAmountInvoiceCurrency > 0) {
            if ($paymentAmountInvoiceCurrency > $remainingBefore + 0.01) {
                throw new \Exception('Cash payment exceeds remaining invoice balance.');
            }

            if (empty($data['treasury_id'])) {
                throw new \Exception('Please select a treasury for cash payment.');
            }

            $category = \App\Models\FinancialCategory::firstOrCreate(
                ['name' => 'Sales Revenue', 'type' => 'income'],
                ['description' => 'System generated category for invoice payments']
            );

            $treasuryAmount = $paymentAmountInvoiceCurrency * $exchangeRate;

            $this->transactionService->recordIncome(
                $treasuryAmount,
                $data['treasury_id'],
                $category->id,
                "Payment for Invoice #{$invoice->code}",
                $invoice,
                $invoice->project_id ?? null,
                $data['date']
            );

            $invoice->increment('paid_amount', $paymentAmountInvoiceCurrency);

            $this->loyaltyService->awardPoints(
                $invoice->customer,
                $paymentAmountInvoiceCurrency,
                null,
                "Points Earned from Invoice #{$invoice->code}",
                $invoice
            );
        }

        // Update Status
        if ($invoice->paid_amount >= $invoice->total) {
            $invoice->update(['status' => 'paid']);
        } elseif ($invoice->paid_amount > 0) {
            $invoice->update(['status' => 'partial']);
        }
    }

    public function cancel(Invoice $invoice)
    {
        if ($invoice->status == 'cancelled') {
            return back()->with('error', 'Invoice is already cancelled.');
        }

        if ($invoice->paid_amount > 0) {
            return back()->with('error', 'Cannot cancel an invoice with payments. Please delete transactions first.');
        }

        $invoice->update(['status' => 'cancelled']);

        // Cancel commission
        \App\Models\SalesCommission::where('invoice_id', $invoice->id)->update(['status' => 'cancelled']);

        return back()->with('success', 'Invoice cancelled and commission reversed.');
    }

    public function printPayment(Invoice $invoice, FinancialTransaction $transaction)
    {
        $settings = Setting::getGroup('general');
        return view('accounting.invoices.print_payment', compact('invoice', 'transaction', 'settings'));
    }

    public function destroy(Invoice $invoice)
    {
        if ($invoice->paid_amount > 0) {
            return back()->with('error', 'Cannot delete an invoice with recorded payments. Please cancel it instead.');
        }

        // Delete related commission
        \App\Models\SalesCommission::where('invoice_id', $invoice->id)->delete();

        // Delete items
        $invoice->items()->delete();

        // Delete invoice
        $invoice->delete();

        return redirect()->route('accounting.invoices.index')
            ->with('success', 'Invoice deleted successfully.');
    }

    public function syncWhmcs(\App\Services\WhmcsService $whmcs)
    {
        $result = $whmcs->syncAllInvoices();

        if ($result['success']) {
            return back()->with('success', "Successfully synced {$result['synced']} invoices from WHMCS.");
        } else {
            return back()->with('error', "Failed to sync WHMCS: " . $result['message']);
        }
    }
}
