<?php

namespace App\Http\Controllers\Expenses;

use App\Enums\App;
use App\Http\Controllers\Controller;
use App\Http\Requests\ExpenseRequest;
use App\Models\Expenses\Expense;
use App\Models\Expenses\ExpenseItem;
use App\Models\Expenses\ExpenseItemMaster;
use App\Models\Prefix;
use App\Services\AccountTransactionService;
use App\Services\PaymentTransactionService;
use App\Services\PaymentTypeService;
use App\Traits\FormatNumber;
use Illuminate\Contracts\View\View;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Session;
use Mpdf\Mpdf;
use Yajra\DataTables\Facades\DataTables;

class ExpenseController extends Controller
{
    use FormatNumber;

    protected $companyId;

    private $accountTransactionService;

    private $paymentTypeService;

    private $paymentTransactionService;

    private $paidPaymentTotal;

    public function __construct(AccountTransactionService $accountTransactionService,
        PaymentTypeService $paymentTypeService,
        PaymentTransactionService $paymentTransactionService)
    {
        $this->companyId = App::APP_SETTINGS_RECORD_ID->value;
        $this->accountTransactionService = $accountTransactionService;
        $this->paymentTypeService = $paymentTypeService;
        $this->paymentTransactionService = $paymentTransactionService;
    }

    /**
     * Create a new expense.
     *
     * @return \Illuminate\View\View
     */
    public function create(): View
    {
        $prefix = Prefix::findOrNew($this->companyId);
        $lastCountId = $this->getLastCountId();
        $selectedPaymentTypesArray = json_encode($this->paymentTypeService->selectedPaymentTypesArray());
        $data = [
            'prefix_code' => $prefix->expense,
            'count_id' => ($lastCountId + 1),
        ];

        return view('expenses.expense.create', compact('data', 'selectedPaymentTypesArray'));

    }

    /**
     * Get last count ID
     * */
    public function getLastCountId()
    {
        return Expense::select('count_id')->orderBy('id', 'desc')->first()?->count_id ?? 0;
    }

    /**
     * List the Accounts
     *
     * @return \Illuminate\View\View
     */
    public function list(): View
    {
        return view('expenses.expense.list');
    }

    /**
     * Print expenses
     *
     * @param  int  $id,  the ID of the order
     * @return \Illuminate\View\View
     */
    public function details($id): View
    {

        $expense = Expense::with('category')->find($id);

        // Item Details
        $expenseItems = ExpenseItem::with('itemDetails')->where('expense_id', $id)->get();

        // Payment Details
        $selectedPaymentTypesArray = json_encode($this->paymentTransactionService->getPaymentRecordsArray($expense));

        return view('expenses.expense.details', compact('expense', 'expenseItems', 'selectedPaymentTypesArray'));
    }

    /**
     * Print Stock Transfer
     *
     * @param  int  $id,  the ID of the sale
     * @return \Illuminate\View\View
     */
    public function print($id, $isPdf = false): View
    {

        $expense = Expense::with('category')->find($id);

        // Item Details
        $expenseItems = ExpenseItem::with('itemDetails')->where('expense_id', $id)->get();

        // Payment Details
        $selectedPaymentTypesArray = json_encode($this->paymentTransactionService->getPaymentRecordsArray($expense));

        $printData = [
            'name' => __('expense.expense'),
        ];

        return view('print.expense.print', compact('isPdf', 'expense', 'expenseItems', 'selectedPaymentTypesArray', 'printData'));

    }

    /**
     * Generate PDF using View: print() method
     * */
    public function generatePdf($id, $destination = 'D')
    {
        $random = uniqid();

        $html = $this->print($id, isPdf: true);

        $mpdf = new Mpdf([
            'mode' => 'utf-8',
            'format' => 'A4',
            'margin_left' => 2,
            'margin_right' => 2,
            'margin_top' => 2,
            'margin_bottom' => 2,
            'default_font' => 'dejavusans',
            // 'direction' => 'rtl',
        ]);

        $mpdf->showImageErrors = true;
        $mpdf->WriteHTML($html);
        /**
         * Display in browser
         * 'I'
         * Downloadn PDF
         * 'D'
         * Return String
         * 'S'
         * File Save
         * 'F'
         * */
        $fileName = 'Expense-'.$id.'-'.$random.'.pdf';

        $mpdf->Output($fileName, $destination);

    }

    /**
     * Edit a expenses.
     *
     * @param  int  $id  The ID of the expense to edit.
     * @return \Illuminate\View\View
     */
    public function edit($id): View
    {
        $expense = Expense::find($id);

        // Item Details
        $expenseItems = ExpenseItem::with('itemDetails')->where('expense_id', $id)->get()->toArray();
        $expenseItemsJson = json_encode($expenseItems);

        // Payment Details
        $selectedPaymentTypesArray = json_encode($this->paymentTransactionService->getPaymentRecordsArray($expense));

        return view('expenses.expense.edit', compact('expense', 'expenseItemsJson', 'selectedPaymentTypesArray'));
    }

    /**
     * Return JsonResponse
     * */
    public function store(ExpenseRequest $request): JsonResponse
    {
        try {
            DB::beginTransaction();
            // Get the validated data from the expenseRequest
            $validatedData = $request->validated();

            if ($request->operation == 'save') {
                // Create a new expense record using Eloquent and save it
                $newExpense = Expense::create($validatedData);

                $request->request->add(['expense_id' => $newExpense->id]);

            } else {
                ExpenseItem::where('expense_id', $validatedData['expense_id'])->delete();

                $fillableColumns = [
                    'expense_category_id' => $validatedData['expense_category_id'],
                    'expense_subcategory_id' => $validatedData['expense_subcategory_id'],
                    'expense_date' => $validatedData['expense_date'],
                    'prefix_code' => $validatedData['prefix_code'],
                    'count_id' => $validatedData['count_id'],
                    'expense_code' => $validatedData['expense_code'],
                    'note' => $validatedData['note'],
                    'round_off' => $validatedData['round_off'],
                    'grand_total' => $validatedData['grand_total'],
                ];
                // First, find the expense
                $newExpense = Expense::findOrFail($validatedData['expense_id']);

                $newExpense->accountTransaction()->delete();
                // Load Expense Payment Transactions
                $paymentTransactions = $newExpense->paymentTransaction;

                foreach ($paymentTransactions as $paymentTransaction) {
                    // Delete Account Transaction
                    $paymentTransaction->accountTransaction()->delete();

                    // Delete Expense Payment Transaction
                    $paymentTransaction->delete();
                }

                // Update the Expense records
                $newExpense->update($fillableColumns);
            }

            $request->request->add(['modelName' => $newExpense]);

            /**
             * Save Table Items in Expense Items Table
             * */
            $expenseItemsArray = $this->saveExpenseItems($request);
            if (! $expenseItemsArray['status']) {
                return response()->json([
                    'status' => false,
                    'message' => $expenseItemsArray['message'],
                ], 409);
            }

            /**
             * Save Expense Payment Records
             * */
            $expensePaymentsArray = $this->saveExpensePayments($request);
            if (! $expensePaymentsArray['status']) {
                return response()->json([
                    'status' => false,
                    'message' => $expensePaymentsArray['message'],
                ], 409);
            }

            /**
             * Payment Should be equal to Grand Total
             * */
            $this->paidPaymentTotal = ($request->modelName->fresh())->paymentTransaction->sum('amount');
            if ($request->grand_total != $this->paidPaymentTotal) {
                return response()->json([
                    'status' => false,
                    'message' => __('payment.paid_payment_not_equal_to_grand_total'),
                ], 409);
            }

            /**
             * Update Expenses Model
             * Total Paid Amunt
             * */
            if (! $this->paymentTransactionService->updateTotalPaidAmountInModel($request->modelName)) {
                throw new \Exception(__('payment.failed_to_update_paid_amount'));
            }

            /**
             * Update Account Transaction entry
             * Call Services
             *
             * @return bool
             * */
            $accountTransactionStatus = $this->accountTransactionService->expenseAccountTransaction($request->modelName);
            if (! $accountTransactionStatus) {
                return response()->json([
                    'status' => false,
                    'message' => __('payment.failed_to_update_account'),
                ], 409);
            }

            DB::commit();

            // Regenerate the CSRF token
            // Session::regenerateToken();

            return response()->json([
                'status' => false,
                'message' => __('app.record_saved_successfully'),
                'id' => $request->expense_id,

            ]);

        } catch (\Exception $e) {
            DB::rollback();

            return response()->json([
                'status' => true,
                'message' => __('app.something_went_wrong').__('app.check_custom_log_file').$e->getMessage(),
            ], 409);

        }

    }

    public function saveExpensePayments($request)
    {
        $paymentCount = $request->row_count_payments;

        for ($i = 0; $i <= $paymentCount; $i++) {

            /**
             * If array record not exist then continue forloop
             * */
            if (! isset($request->payment_amount[$i])) {
                continue;
            }

            /**
             * Data index start from 0
             * */
            $amount = $request->payment_amount[$i];

            if ($amount > 0) {
                if (! isset($request->payment_type_id[$i])) {
                    return [
                        'status' => false,
                        'message' => __('payment.missed_to_select_payment_type').'#'.$i,
                    ];
                }

                $paymentsArray = [
                    'transaction_date' => $request->expense_date,
                    'amount' => $amount,
                    'payment_type_id' => $request->payment_type_id[$i],
                    'note' => $request->payment_note[$i],
                ];

                if (! $transaction = $this->paymentTransactionService->recordPayment($request->modelName, $paymentsArray)) {
                    throw new \Exception(__('payment.failed_to_record_payment_transactions'));
                }

            }// amount>0
        }// for end

        return ['status' => true];
    }

    public function saveExpenseItems($request)
    {
        $itemsCount = $request->row_count;

        for ($i = 0; $i < $itemsCount; $i++) {
            /**
             * If array record not exist then continue forloop
             * */
            if (! isset($request->name[$i])) {
                continue;
            }

            /**
             * Data index start from 0
             * */
            $itemName = $request->name[$i];
            $itemQuantity = $request->quantity[$i];

            if (empty($itemQuantity) || $itemQuantity === 0 || $itemQuantity < 0) {
                return [
                    'status' => false,
                    'message' => ($itemQuantity < 0) ? __('item.item_qty_negative', ['item_name' => $itemName]) : __('item.please_enter_item_quantity', ['item_name' => $itemName]),
                ];
            }

            $itemsArray = [
                'expense_id' => $request->expense_id,
                'expense_item_master_id' => $this->getExpenseItemId($request, index: $i),
                'description' => $request->description[$i],
                'unit_price' => $request->unit_price[$i],
                'quantity' => $itemQuantity,
                'total' => $request->total[$i],
            ];

            if (! ExpenseItem::create($itemsArray)) {
                return ['status' => false];
            }

        }// for end

        return ['status' => true];
    }

    /**
     * If record not exist then create
     * */
    protected function getExpenseItemId($request, $index)
    {
        $itemName = $request->name[$index];
        $itemUnitPrice = $request->unit_price[$index];

        $existingItem = ExpenseItemMaster::where('name', $itemName)->first();

        if ($existingItem) {
            return $existingItem->id;
        }

        $newItem = ExpenseItemMaster::create(['name' => $itemName, 'unit_price' => $itemUnitPrice]);

        return $newItem->id;
    }

    public function datatableList(Request $request)
    {

        $data = Expense::with('user', 'paymentTransaction.paymentType', 'category', 'subcategory')
            ->when($request->expense_category_id, function ($query) use ($request) {
                return $query->where('expense_category_id', $request->expense_category_id);
            })
            ->when(! auth()->user()->can('expense.can.view.other.users.expenses'), function ($query) {
                return $query->where('created_by', auth()->user()->id);
            });

        return DataTables::of($data)
            ->addIndexColumn()
            ->addColumn('created_at', function ($row) {
                return $row->created_at->format(app('company')['date_format']);
            })
            ->addColumn('username', function ($row) {
                return $row->user->username ?? '';
            })
            ->addColumn('expense_date', function ($row) {
                return $row->formatted_expense_date;
            })
            ->addColumn('paid_amount', function ($row) {
                return $this->formatWithPrecision($row->paid_amount);
            })
            ->addColumn('expense_number', function ($row) {
                return $row->expense_code;
            })
            ->addColumn('payment_type', function ($row) {
                return $row->paymentTransaction->pluck('paymentType.name')->implode(', ');
            })
            ->addColumn('expense_category', function ($row) {
                return $row->category->name;
            })
            ->addColumn('expense_subcategory', function ($row) {
                return $row->subcategory->name ?? '';
            })
            ->addColumn('action', function ($row) {
                $id = $row->id;

                $editUrl = route('expense.edit', ['id' => $id]);
                $deleteUrl = route('expense.delete', ['id' => $id]);
                $detailsUrl = route('expense.details', ['id' => $id]);
                $printUrl = route('expense.print', ['id' => $id]);
                $pdfUrl = route('expense.pdf', ['id' => $id]);

                $actionBtn = '<div class="dropdown ms-auto">
                            <a class="dropdown-toggle dropdown-toggle-nocaret" href="#" data-bs-toggle="dropdown"><i class="bx bx-dots-vertical-rounded font-22 text-option"></i>
                            </a>
                            <ul class="dropdown-menu">
                                <li>
                                    <a class="dropdown-item" href="'.$editUrl.'"><i class="bi bi-trash"></i><i class="bx bx-edit"></i> '.__('app.edit').'</a>
                                </li>
                                <li>
                                    <a class="dropdown-item" href="'.$detailsUrl.'"></i><i class="bx bx-receipt"></i> '.__('app.details').'</a>
                                </li>
                                <li>
                                    <a target="_blank" class="dropdown-item" href="'.$printUrl.'"></i><i class="bx bx-printer "></i> '.__('app.print').'</a>
                                </li>
                                <li>
                                    <a target="_blank" class="dropdown-item" href="'.$pdfUrl.'"></i><i class="bx bxs-file-pdf"></i> '.__('app.pdf').'</a>
                                </li>
                                <li>
                                    <button type="button" class="dropdown-item text-danger deleteRequest" data-delete-id='.$id.'><i class="bx bx-trash"></i> '.__('app.delete').'</button>
                                </li>
                            </ul>
                        </div>';

                return $actionBtn;
            })
            ->rawColumns(['action'])
            ->make(true);
    }

    public function delete(Request $request): JsonResponse
    {

        DB::beginTransaction();

        $selectedRecordIds = $request->input('record_ids');

        // Perform validation for each selected record ID
        foreach ($selectedRecordIds as $recordId) {
            $record = Expense::find($recordId);
            if (! $record) {
                // Invalid record ID, handle the error (e.g., show a message, log, etc.)
                return response()->json([
                    'status' => false,
                    'message' => __('app.invalid_record_id', ['record_id' => $recordId]),
                ]);

            }
            // You can perform additional validation checks here if needed before deletion
        }

        /**
         * All selected record IDs are valid, proceed with the deletion
         * Delete all records with the selected IDs in one query
         * */
        try {
            // Attempt deletion (as in previous responses)
            Expense::whereIn('id', $selectedRecordIds)->chunk(100, function ($expenses) {
                foreach ($expenses as $expense) {
                    $expense->accountTransaction()->delete();
                    // Load Expense Payment Transactions
                    $paymentTransactions = $expense->paymentTransaction;
                    foreach ($paymentTransactions as $paymentTransaction) {
                        // Delete Payment Account Transactions
                        $paymentTransaction->accountTransaction()->delete();

                        // Delete Expense Payment Transactions
                        $paymentTransaction->delete();
                    }
                }
            });

            // Delete Expenses
            $deletedCount = Expense::whereIn('id', $selectedRecordIds)->delete();

            DB::commit();

            return response()->json([
                'status' => true,
                'message' => __('app.record_deleted_successfully'),
            ]);
        } catch (\Illuminate\Database\QueryException $e) {
            DB::rollback();

            if ($e->getCode() == 23000) {
                return response()->json([
                    'status' => false,
                    'message' => __('app.cannot_delete_records'),
                ], 409);
            }
        }
    }

    /**
     * Ajax Response
     * Search Bar list
     * */
    public function getAjaxSearchBarList()
    {
        $search = request('search');

        $expenseItemsMaster = ExpenseItemMaster::where('name', 'LIKE', "%{$search}%")
            ->select('id', 'name', 'unit_price') // Select only the required columns
            ->limit(10)
            ->get();
        $response = [
            'results' => $expenseItemsMaster->map(function ($item) {
                return [
                    'id' => $item->id,
                    'text' => $item->name,
                    'unit_price' => $item->unit_price,
                ];
            })->toArray(),
        ];

        return json_encode($response);
    }
}
