<?php

namespace App\Http\Controllers\Admin\Storage;

use App\Exports\StorageExport;
use App\Models\Storage;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\StorageRequest;
use App\Imports\StorageImport;
use App\Models\Brand;
use App\Models\PaymentMeta;
use App\Models\Product;
use App\Services\Admin\Products\ProductService;
use App\Services\Admin\Storage\StorageService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;
use Inertia\Inertia;
use Maatwebsite\Excel\Facades\Excel;

class StorageController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index(Request $request, StorageService $storageService)
    {
        /** check payment meta */
        if (!$storageService->checkPaymentMeta())
            return redirect()->back()->withErrors([__('messages.payment_meta_incomplete')]);

        // exchange rate
        $exchangeRate = PaymentMeta::where('store_id', null)->first();
        $exchangeRate = $exchangeRate != null && $exchangeRate->exchange_rate != null ? (int)$exchangeRate->exchange_rate : null;

        return Inertia::render('Admin/Storage/List', [
            'storages' => $storageService->getStorages($request),
            'exchangeRate' => $exchangeRate
        ]);
    }

    /**
     * Show the form for creating a new resource.
     */
    public function create()
    {
        //
    }

    /**
     * Store a newly created resource in storage.
     */
    public function store(StorageRequest $request, StorageService $storageService)
    {
        /** check payment meta */
        if (!$storageService->checkPaymentMeta())
            return redirect()->back()->withErrors([__('messages.payment_meta_incomplete')]);

        // create storage
        Storage::create([
            'uuid' => 'storage-' . rand(1000000, 9999999),
            'name' => $request->name,
            'address' => $request->address,
        ]);

        /** Report Action */
        $this->report(__("messages.report_storage_created", ['storage' => $request->name]));

        return redirect()->route('admin.storages.index')->with('message', [__('messages.storage_created')]);
    }

    /**
     * Display the specified resource.
     */
    public function show(Storage $storage, Request $request, StorageService $storageService, ProductService $productService)
    {
        return Inertia::render('Admin/Storage/Show', [
            'storage' => $storage,
            'products' => $storageService->getStorageProducts($request, $storage),
            'categories' => $productService->getCategoriesBranch(),
            'brands' => Brand::where('status', 'publish')->get()->map(fn($brand) => [
                'id' => $brand->id,
                'title' => $brand->title,
            ]),
        ]);
    }

    /**
     * Show the form for editing the specified resource.
     */
    public function edit(Storage $storage)
    {
        //
    }

    /**
     * Update the specified resource in storage.
     */
    public function update(StorageRequest $request, Storage $storage)
    {
        // update storage
        $storage->update([
            'name' => $request->name,
            'address' => $request->address,
        ]);

        /** Report Action */
        $this->report(__("messages.report_storage_updated", ['storage' => $storage->name]));

        return redirect()->route('admin.storages.index')->with('message', [__('messages.storage_updated')]);
    }

    /**
     * Remove the specified resource from storage.
     */
    public function destroy(Storage $storage)
    {
        $storageName = $storage->name;

        if ($storage->product_count > 0)
            return redirect()->route('admin.storages.index')->withErrors([__('messages.storage_has_product_can_not_delete')]);

        // delete storage
        $storage->delete();

        /** Report Action */
        $this->report(__("messages.report_storage_deleted", ['storage' => $storageName]));

        return redirect()->route('admin.storages.index')->with('message', [__('messages.storage_deleted')]);
    }

    /**
     * storage inventories list
     */
    public function storageList(Storage $storage, Product $product)
    {
        // inventories list
        $inventories = $product->inventories()->where('storage_id', $storage->id)->orderBy('created_at', 'desc')->get();

        // other inventories count
        $inventories_count = [
            'you' => $product->inventories()->where('store_id', null)->get()->count() - count($inventories),
            'other' => $product->inventories()->get()->count() - ($product->inventories()->where('store_id', null)->get()->count()),
        ];

        return Inertia::render('Admin/Storage/Inventory/StorageList', [
            'storage' => $storage,
            'product' => [
                'id' => $product->id,
                'uuid' => $product->uuid,
                'title' => $product->title,
                'slug' => $product->slug,
                'get_images' => $product->get_images,
                'price_model' => $product->price_model,
                'is_vip' => $product->is_vip,
                'commission' => $product->commission,
            ],
            'inventories' => $inventories,
            'inventories_count' => $inventories_count
        ]);
    }

    /**
     * export storage
     */
    public function export(Storage $storage, Request $request)
    {
        $request->validate([
            'type' => 'required',
            'category' => 'required',
            'brand' => 'required',
        ]);

        /** Report Action */
        $this->report(__("messages.report_storage_exported", ['storage' => $storage->name]));

        return Excel::download(new StorageExport($storage, $request->type, $request->category, $request->brand), 'خروجی انبار ' . $storage->name . '.xlsx');
    }

    /**
     * import storage
     */
    public function import(Storage $storage, Request $request)
    {
        $request->validate([
            'file' => 'required',
        ]);

        /** Report Action */
        $this->report(__("messages.report_storage_imported", ['storage' => $storage->name]));

        Excel::import(new StorageImport($storage), $request->file('file'));

        return redirect()->back()->with('message', [__('messages.excel_imported_to_storage')]);
    }

    /**
     * exchange rate update
     */
    public function exchangeRateUpdate(Request $request)
    {
        $request->validate([
            'rate' => 'required',
        ]);

        // lock update process for ? minute
        $lockKey = 'exchange_rate_update_lock';
        $lockTime = 300; // 5 minute
        if (Cache::has($lockKey)) {
            return redirect()->back()->withErrors([__('messages.exchange_rate_is_every_5_minutes')]);
        }

        // get rate input
        $rate = (int)str_replace([',', '،'], '', $request->input('rate'));

        // save exchange new rate
        PaymentMeta::where('store_id', null)->update([
            'exchange_rate' => $rate,
        ]);

        // last_id and Batch size for continue updating process
        $chunk = 100000;
        $lastId = (int) ($request->query('last_id') ?? 0);

        // contine if browser is closed
        ignore_user_abort(true);
        set_time_limit(0);

        // update for inventories with store_id = ? - (null for main store)
        $storeIdFilter = null;
        $storeCondition = is_null($storeIdFilter) ? 'store_id IS NULL' : 'store_id = ?';

        // create query binding
        $bindings = [$rate, $rate, $lastId];
        if (!is_null($storeIdFilter)) {
            $bindings[] = $storeIdFilter;
        }

        // Bulk update batch
        $affected = DB::update("
            UPDATE inventories
            SET
                price = CASE
                    WHEN is_update_with_currency = 1
                        AND currency_price IS NOT NULL
                        AND currency_price != 0
                    THEN ROUND(currency_price * ?, 0)
                    ELSE price
                END,

                discount_price = CASE
                    WHEN is_update_with_currency = 1
                        AND currency_discount_price IS NOT NULL
                        AND currency_discount_price != 0
                    THEN ROUND(currency_discount_price * ?, 0)
                    ELSE discount_price
                END
            WHERE id > ?
            AND $storeCondition
            ORDER BY id
            LIMIT $chunk
        ", $bindings);

        // Get last processed ID safely
        $query = DB::table('inventories')->where('id', '>', $lastId);
        if (is_null($storeIdFilter)) {
            $query->whereNull('store_id');
        } else {
            $query->where('store_id', $storeIdFilter);
        }
        $lastProcessedId = $query
            ->orderBy('id')
            ->limit($chunk)
            ->max('id');

        // if there are still records left, make the next request async
        if ($affected > 0 && $lastProcessedId) {
            $nextLastId = $lastProcessedId;
            $url = route('admin.storages.exchange_rate_update', [
                'last_id' => $nextLastId
            ]);

            // small delay to reduce server pressure
            usleep(500000); // 0.5 sec

            $ch = curl_init($url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, false);
            curl_setopt($ch, CURLOPT_TIMEOUT_MS, 1);
            curl_exec($ch);
            curl_close($ch);
        }

        // delete cache and views
        try {
            File::deleteDirectory(storage_path('framework/cache/data'));
            Cache::flush();
            $views = File::files(storage_path('framework/views'));
            foreach ($views as $view) {
                File::delete($view);
            }
        } catch (\Exception $e) {
        }

        // lock update process for ? minute
        Cache::put($lockKey, time(), $lockTime);

        // return response immediately
        if ($lastId === 0) {
            return redirect()->back()->with('message', [__('messages.an_order_was_issued_to_update_prices_based_on_the_exchange_rate')]);
        }

        exit;
    }
}
