<?php

namespace App\Http\Controllers;

use App\Models\invoice\Deposit;
use App\Models\invoice\Invoice;
use App\Models\invoice\Fishtank;
use App\Models\invoice\ArPayment;
use App\Http\Controllers\GetAPIController;
use Illuminate\Validation\Rule;
use Illuminate\Support\Carbon;
use Illuminate\View\View;
use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\Log;
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;
use App\Helpers\ActivityLogger;

class InvoiceController extends Controller
{
    /**
     * Permission Constructor
     */
    public function __construct()
    {
        $this->middleware('permission:invoice-view', ['only' => ['index', 'show']]);
        $this->middleware('permission:invoice-add', ['only' => ['create', 'store']]);
        $this->middleware('permission:invoice-edit', ['only' => ['edit', 'update']]);
        $this->middleware('permission:invoice-delete', ['only' => ['destroy']]);
    }

    /**
     * Display a listing of the resource.
     */
    public function index(Request $request)
    {
        Session::flash('backUrl', $request->fullUrl());
        return view('invoice.index');
    }

    /**
     * Show the form for creating a new resource.
     */
    public function create(): View
    {
        if (Session::has('backUrl')) {
            Session::keep('backUrl');
        }
        $paymentMethod = (new GetAPIController())->getPaymentMethod();
        $debtorDetail = (new GetAPIController())->getDebtorDetail();
        $itemPrice = (new GetAPIController())->getItemPrice();

        $itemMapper = $this->mapItems($itemPrice['result']);
        return view('invoice.create', compact('paymentMethod', 'debtorDetail', 'itemPrice', 'itemMapper'));
    }

    /**
     * Store a newly created resource in storage.
     */
    public function store(Request $request): RedirectResponse
    {
        if (Session::has('backUrl')) {
            Session::keep('backUrl');
        }
        //Input Validation
        $validatedData = $this->InputValidate($request);
        $debtorData = $validatedData['debtorData'];
        $depositData = $validatedData['depositData'];
        $paymentData = $validatedData['paymentData'];
        $fishtankData = $validatedData['fishtankData'];
        $statusID = $validatedData['statusID'];

        if ($statusID == 2 && $debtorData['total_amount'] == 0) {
            $notification = [
                'message' => 'Quantity cannot be all 0',
                'alert-type' => 'error',
            ];
            return redirect()->back()->withInput()->with($notification);
        }

        //Store in database table invoice
        $dbFormattedDate = Carbon::createFromFormat('d/m/Y', $debtorData['date'])->format('Y-m-d');
        $invoice = Invoice::create([
            'debtorCode' => $debtorData['debtorCode'],
            'debtorName' => $debtorData['debtorName'],
            'vechicleNo' => '-',
            'invoiceNo' => $debtorData['invoiceNo'],
            'date' => $dbFormattedDate,
            'totalQuantity' => $debtorData['total_quantity'],
            'totalAmount' => $debtorData['total_amount'],
            'statusID' => $debtorData['statusID'],
        ]);

        ActivityLogger::logInvoiceCreated($invoice, $statusID);

        //Store in database table deposit
        $depositData['InvoiceID'] = $invoice->id;
        $deposit = new Deposit($depositData);
        $deposit->save();

        //Store in database table ar_payment
        $payment = ArPayment::create([
            'debtor' => $debtorData['debtorCode'],
            'paymentMethod' => $paymentData['invoicePaymentMethod'],
            'paymentAmount' => $debtorData['total_amount'],
            'InvoiceID' => $paymentData['InvoiceID'] = $invoice->id,
        ]);

        //Store in the database table fishtank_invoice
        $itemPrice = (new GetAPIController())->getItemPrice();
        $itemMapper = $this->mapItems($itemPrice['result']);
        $fishTanks = [];
        foreach ($itemMapper as $item) {
            $itemCode = $item['itemCode'];
            $itemPrice = $item['itemPrice'];
            $quantityKey = $item['itemQty'];

            $quantity = $fishtankData[$quantityKey];
            if ($quantity > 0) {
                Fishtank::create([
                    'InvoiceID' => $invoice->id,
                    'fishTanks' => $itemCode,
                    'quantity' => $quantity,
                    'unitPrice' => $itemPrice,
                ]);
                $fishTank = [
                    "itemCode" => $item['itemOriCode'],
                    "quantity" => $quantity,
                    "unitPrice" => $itemPrice
                ];
                $fishTanks[] = $fishTank;
            }
        }

        // Determine the redirection route based on the status
        $redirectTo = ($statusID == 2) ? 'admin.invoice.show' : 'admin.invoice.index';

        $notification = [
            'message' => 'Invoice created successfully.',
            'alert-type' => 'success'
        ];

        if ($statusID == 2) {
            $invoicePost = [
                "debtorCode" => $invoice->debtorCode,
                "documentNo" => "KPBIV-".$invoice->invoiceNo,
                "documentDate" => $invoice->date,
                "items" => $fishTanks
            ];
            $response = (new GetAPIController())->postSalesInvoice($invoicePost);

            if (!$response['success']) {
                $notification = [
                    'message' => 'Invoice created but AutoCount has error. <br/> Report to admin with the following message <br/> ' . $response['result'],
                    'alertType' => 'error',
                ];
                return redirect()->back()->withInput()->with($notification);
            }

            $arPayment = [
                "debtorCode" => $invoice->debtorCode,
                "paymentMethod" => $payment->paymentMethod,
                "docDate" => $invoice->date,
                "amount" => $invoice->totalAmount,
                "chequeNo" => null,
                "knockOffItems" => [[
                    "invoiceNo" => "KPBIV-".$invoice->invoiceNo,
                    "amount" => $invoice->totalAmount
                ]]
            ];
            $response = (new GetAPIController())->postARPayment($arPayment);
            if (!$response['success']) {
                $notification = [
                    'message' => 'Invoice created but AutoCount has error. <br/> Report to admin with the following message <br/> ' . $response['result'],
                    'alertType' => 'error',
                ];
                return redirect()->back()->withInput()->with($notification);
            } else {
                $payment->paymentNo = $response['result'];
                $payment->save();
            }

            if ($depositData['depositNo'] != null) {
                $depositPost = [
                    "documentNo" => $deposit->depositNo,
                    "documentDate" => $invoice->date,
                    "depositMethod" => "CUSTOMER DEPOSIT",
                    "customerAccount" => $invoice->debtorCode,
                    "items" => [
                        [
                            "paymentMethod" => $deposit->paymentMethod,
                            "depositAmount" => floatval($deposit->depositAmount)
                        ]
                    ]
                ];
                $response = (new GetAPIController())->postDeposit($depositPost);
                if (!$response['success']) {
                    $notification = [
                        'message' => 'Invoice created but AutoCount has error. <br/> Report to admin with the following message <br/> ' . $response['result'],
                        'alertType' => 'error',
                    ];
                    return redirect()->back()->withInput()->with($notification);
                }
            }
        }

        if (auth()->user()->can('invoice-view')) {
            return redirect()->route($redirectTo, ['invoice' => $invoice->id])
                ->with($notification);
        } else {
            return redirect()->route('admin.dashboard')->with($notification);
        }
    }

    /**
     * Display the specified resource.
     */
    public function show(Invoice $invoice): View
    {
        if (Session::has('backUrl')) {
            Session::keep('backUrl');
        }
        $deposits = Deposit::where('invoiceID', $invoice->id)
            ->get();

        $fishtanks = Fishtank::where('invoiceID', $invoice->id)
            ->get();

        $payments = ArPayment::where('invoiceID', $invoice->id)
            ->get();

        $formattedDate = Carbon::parse($invoice['date'])->format('d/m/Y');

        $fishtankQuantities = $fishtanks->pluck('quantity', 'fishTanks');

        $itemPrice = (new GetAPIController())->getItemPrice();
        $itemMapper = $this->mapItems($itemPrice['result']);

        return view('invoice.show', compact('invoice', 'deposits', 'fishtanks', 'payments', 'formattedDate', 'itemMapper', 'fishtankQuantities'));
    }

    /**
     * Show the form for editing the specified resource.
     */
    public function edit(Invoice $invoice): View
    {
        if (Session::has('backUrl')) {
            Session::keep('backUrl');
        }
        $formattedDate = Carbon::parse($invoice['date'])->format('d/m/Y');

        $paymentMethod = (new GetAPIController())->getPaymentMethod();
        $debtorDetail = (new GetAPIController())->getDebtorDetail();

        $itemPrice = (new GetAPIController())->getItemPrice();
        $itemMapper = $this->mapItems($itemPrice['result']);

        $deposits = Deposit::where('invoiceID', $invoice->id)->get();
        $fishtanks = Fishtank::where('invoiceID', $invoice->id)->get();
        $payments = ArPayment::where('invoiceID', $invoice->id)->get();

        $fishtankQuantities = $fishtanks->pluck('quantity', 'fishTanks');

        return view('invoice.update', compact('invoice', 'formattedDate', 'deposits', 'fishtanks', 'payments', 'paymentMethod', 'debtorDetail', 'itemPrice', 'itemMapper', 'fishtankQuantities'));
    }

    /**
     * Update the specified resource in storage.
     */
    public function update(Request $request, Invoice $invoice): RedirectResponse
    {
        if (Session::has('backUrl')) {
            Session::keep('backUrl');
        }
        //Input Validation
        $validatedData = $this->InputValidate($request, $invoice);
        $debtorData = $validatedData['debtorData'];
        $depositData = $validatedData['depositData'];
        $paymentData = $validatedData['paymentData'];
        $fishtankData = $validatedData['fishtankData'];
        $statusID = $validatedData['statusID'];
        $dbFormattedDate = Carbon::createFromFormat('d/m/Y', $debtorData['date'])->format('Y-m-d');

        if ($statusID == 2 && $debtorData['total_amount'] == 0) {
            $notification = [
                'message' => 'Quantity cannot be all 0',
                'alert-type' => 'error',
            ];
            return redirect()->back()->withInput()->with($notification);
        }

        // update invoice
        $invoice->update([
            'debtorCode' => $debtorData['debtorCode'],
            'debtorName' => $debtorData['debtorName'],
            'vechicleNo' => '-',
            'invoiceNo' => $debtorData['invoiceNo'],
            'date' => $dbFormattedDate,
            'totalQuantity' => $debtorData['total_quantity'],
            'totalAmount' => $debtorData['total_amount'],
            'statusID' => $debtorData['statusID'],
        ]);

        ActivityLogger::logInvoiceUpdated($invoice, $statusID);

        //update deposit
        $deposit = Deposit::where('InvoiceID', $invoice->id)->firstOrFail();
        $deposit->update([
            'depositNo' => $depositData['depositNo'],
            'depositAmount' => $depositData['depositAmount'],
            'paymentMethod' => $depositData['paymentMethod'],
            'InvoiceID' => $depositData['InvoiceID'] = $invoice->id,
        ]);

        //update ar_payment
        $payment = ArPayment::where('InvoiceID', $invoice->id)->firstOrFail();
        $payment->update([
            'debtor' => $debtorData['debtorCode'],
            'paymentMethod' => $paymentData['invoicePaymentMethod'],
            'paymentAmount' => $debtorData['total_amount'],
            'InvoiceID' => $paymentData['InvoiceID'] = $invoice->id,
        ]);

        // Update fishtank_invoice
        $itemPrice = (new GetAPIController())->getItemPrice();
        $itemMapper = $this->mapItems($itemPrice['result']);
        $fishTanks = [];

        foreach ($itemMapper as $item) {
            $itemCode = $item['itemCode'];
            $itemPrice = $item['itemPrice'];
            $quantityKey = $item['itemQty'];

            $fishtank = Fishtank::where(['InvoiceID' => $invoice->id, 'fishTanks' => $itemCode])->first();
            $quantity = $fishtankData[$quantityKey];
            if ($fishtank) {
                if ($quantity > 0) {
                    $fishtank->update([
                        'InvoiceID' => $invoice->id,
                        'fishTanks' => $itemCode,
                        'quantity' => $quantity,
                        'unitPrice' => $itemPrice,
                    ]);

                    $fishTank = [
                        "itemCode" => $item['itemOriCode'],
                        "quantity" => $quantity,
                        "unitPrice" => $itemPrice
                    ];
                    $fishTanks[] = $fishTank;

                } else {
                    $fishtank->delete();
                }
            } elseif ($quantity > 0) {
                Fishtank::create([
                    'InvoiceID' => $invoice->id,
                    'fishTanks' => $itemCode,
                    'quantity' => $quantity,
                    'unitPrice' => $itemPrice,
                ]);

                $fishTank = [
                    "itemCode" => $item['itemOriCode'],
                    "quantity" => $quantity,
                    "unitPrice" => $itemPrice
                ];
                $fishTanks[] = $fishTank;
            }
        }

        // Determine the route based on status
        if ($statusID == 2) {
            $redirectTo = 'admin.invoice.show';

            $invoicePost = [
                "debtorCode" => $invoice->debtorCode,
                "documentNo" => "KPBIV-".$invoice->invoiceNo,
                "documentDate" => $invoice->date,
                "items" => $fishTanks
            ];
            $response = (new GetAPIController())->postSalesInvoice($invoicePost);
            if (!$response['success']) {
                $notification = [
                    'message' => 'Invoice created but AutoCount has error. <br/> Report to admin with the following message <br/> ' . $response['result'],
                    'alertType' => 'error',
                ];
                return redirect()->back()->withInput()->with($notification);
            }

            $arPayment = [
                "debtorCode" => $invoice->debtorCode,
                "paymentMethod" => $payment->paymentMethod,
                "docDate" => $invoice->date,
                "amount" => $invoice->totalAmount,
                "chequeNo" => null,
                "knockOffItems" => [[
                    "invoiceNo" => "KPBIV-".$invoice->invoiceNo,
                    "amount" => $invoice->totalAmount
                ]]
            ];
            $response = (new GetAPIController())->postARPayment($arPayment);
            if (!$response['success']) {
                $notification = [
                    'message' => 'Invoice created but AutoCount has error. <br/> Report to admin with the following message <br/> ' . $response['result'],
                    'alertType' => 'error',
                ];
                return redirect()->back()->withInput()->with($notification);
            } else {
                $payment->paymentNo = $response['result'];
                $payment->save();
            }

            if ($depositData['depositNo'] != null) {
                $depositPost = [
                    "documentNo" => $deposit->depositNo,
                    "documentDate" => $invoice->date,
                    "depositMethod" => "CUSTOMER DEPOSIT",
                    "customerAccount" => $invoice->debtorCode,
                    "items" => [
                        [
                            "paymentMethod" => $deposit->paymentMethod,
                            "depositAmount" => floatval($deposit->depositAmount)
                        ]
                    ]
                ];
                $response = (new GetAPIController())->postDeposit($depositPost);
                if (!$response['success']) {
                    $notification = [
                        'message' => 'Invoice created but AutoCount has error. <br/> Report to admin with the following message <br/> ' . $response['result'],
                        'alertType' => 'error',
                    ];
                    return redirect()->back()->withInput()->with($notification);
                }
            }
        } else {
            $redirectTo = 'admin.invoice.index';
        }

        $notification = array(
            'message' => 'Invoice Updated!',
            'alert-type' => 'success'
        );

        return redirect()->route($redirectTo, ['invoice' => $invoice->id])->with($notification);
    }

    /**
     * Remove the specified resource from storage.
     */
    public function destroy(Invoice $invoice)
    {
        if (auth()->user()->can('invoice-delete')) {
            $alertType = 'success';
            $message = 'Invoice deleted successfully';
            $apiMessage = '';

            if ($invoice->statusID == 2) {
                $payment = ArPayment::where('invoiceID', $invoice->id)
                        ->first();
                if($payment) {
                    $deleteARDocNo = [
                        "docNo" => $payment->paymentNo
                    ];
                    $response = (new GetAPIController())->deleteARPayment($deleteARDocNo);
                    $payment->delete();
                } else {
                    $response['success'] = true;
                }

                $deposit = Deposit::where('invoiceID', $invoice->id)
                            ->first();
                if ($deposit) {
                    $deleteDepositDocNo = [
                        "docNo" => $deposit->depositNo
                    ];
                    $depositResponse = (new GetAPIController())->deleteDeposit($deleteDepositDocNo);
                    $deposit->delete();
                }


                if (!$response['success']) {
                    return response()->json([
                        'message' => 'AutoCount error: ' . $response['result'],
                        'alert-type' => 'error'
                    ], 500);
                } else {
                    $deleteDocNo = [
                        "docNo" => "KPBIV-".$invoice->invoiceNo
                    ];
                    $response = (new GetAPIController())->deleteInvoice($deleteDocNo);

                    if (!$response['success']) {
                        return response()->json([
                            'message' => 'AutoCount error: ' . $response['result'],
                            'alert-type' => 'error'
                        ], 500);
                    }
                }
            }
            ActivityLogger::logInvoiceDeleted($invoice);
            $invoice->delete();

            return response()->json([
                'message' => $message,
                'alert-type' => $alertType,
                'apiMessage' => $apiMessage
            ], 200);
        } else {
            return response()->json([
                'message' => 'You do not have permission.',
                'alert-type' => 'warning'
            ], 403);
        }
    }

    /**
     * Input Validation
     */
    private function InputValidate(Request $request, Invoice $invoice = null)
    {
        // Validation for the debtor table
        $debtorData = $request->validate([
            'debtorCode' => 'required|string|max:255',
            'debtorName' => 'required|string|max:255',
            'invoiceNo' => ['required', 'string',
                Rule::unique('invoice', 'invoiceNo')->ignore($invoice, 'id')],
            'date' => 'required|date_format:d/m/Y',
            'total_quantity' => 'numeric|min:0',
            'total_amount' => 'numeric|min:0',
            'statusID' => 'required|in:1,2',
        ]);

        $statusID = $request->statusID;

        // Validation for the deposit table
        $depositData = $request->validate([
            'depositAmount' => 'nullable|numeric|min:0',
            'paymentMethod' => 'nullable|string|max:255',
            'depositNo' => 'nullable|string|max:255',
        ]);

        // Validation for the ar_payment table
        $paymentData = $request->validate([
            'invoicePaymentMethod' => 'required|string|max:255',

        ]);

        // Validation for the fishtank table
        $itemPrice = (new GetAPIController())->getItemPrice();
        $itemMapper = $this->mapItems($itemPrice['result']);
        $validationRules = [];
        foreach ($itemMapper as $item) {
            $validationRules[$item['itemCode']] = 'string';
            $validationRules[$item['itemPrice']] = 'nullable|numeric|min:0';
            $validationRules[$item['itemQty']] = 'nullable|numeric|min:0';
        }
        $fishtankData = $request->validate($validationRules);

        return [
            'debtorData' => $debtorData,
            'depositData' => $depositData,
            'paymentData' => $paymentData,
            'fishtankData' => $fishtankData,
            'statusID' => $statusID,
        ];
    }

    /**
     * Item Mapper
     */
    private function mapItems($items)
    {
        $mappedItems = [];

        $itemMapping = Config::get('itemMapper');
        foreach ($itemMapping as $key => $value) {   //from itemMapper
            foreach ($items as $item) {     //from API json
                if ($item['itemCode'] === $value) {   //match API json with itemMapper
                    $itemCode = $key;
                    $itemPrice = $item['price'];
                    $itemDescription = $item['description'];
                    $itemQty = 'quantity_' . $key;

                    $mappedItem = [
                        'itemCode' => $itemCode,
                        'itemPrice' => $itemPrice,
                        'itemDescription' => $itemDescription,
                        'itemQty' => $itemQty,
                        'itemOriCode' => $value,
                    ];

                    $mappedItems[] = $mappedItem;
                    break;
                }
            }
        }
        return $mappedItems;
    }
}
