<?php

declare(strict_types=1);

namespace App\Http\Controllers\Market;

use App\Enums\UserActions;
use App\Enums\UserTypes;
use App\Http\Controllers\Helpers\Permissions\ProductPermissions;
use App\Http\Controllers\Helpers\Permissions\UserPermissions;
use App\Http\Controllers\Helpers\PhotoURL;
use App\Http\Controllers\Helpers\ResponseHelper;
use App\Http\Controllers\Helpers\UserController;
use App\Http\Controllers\UserManagement\CreateUserControllerUltimate;
use App\Http\Controllers\viewHelperController;
use App\Models\FileList;
use App\Models\Market\Organization;
use App\Models\Market\Product;
use App\Models\User;
use Hypervel\Http\Request;
use Hypervel\Support\Facades\Auth;
use Hypervel\Support\Facades\Response;

use Hypervel\Support\Facades\Hash;
use Hypervel\Support\Facades\Session;

use App\Models\Market\Store;
use Hypervel\Support\Facades\Validator;
use Hypervel\Support\Facades\Config;
use Symfony\Component\HttpFoundation\Test\Constraint\ResponseHeaderLocationSame;

class StoreController
{
    public function getCategories(Request|false $request = false)
    {
        $nonRequest = $request == false;
        $categories = array_keys(Config::get('market.storesCategorySubcategory', []));

        if (!$nonRequest) {
            return response()->json($categories);
        }

        return $categories;
    }

    public function getSubcategories(Request|false $request = false, string|false $category = false)
    {
        $request = $request ?: request();
        $category = $request ? $request->input('category') : $category;

        $categoryList = Config::get('market.storesCategorySubcategory', []);

        if (!$category || !array_key_exists($category, $categoryList)) {
            if ($request) {
                return response()->json([], 400);
            }
            return [];
        }

        $subcategories = $categoryList[$category] ?? [];

        if ($request) {
            return response()->json($subcategories);
        }

        return $subcategories;
    }

    public function store(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'name' => 'required|string|max:255|unique:str,name',
            'description' => 'required|string',
            'address' => 'required|string',
            'category' => 'nullable|string|max:100',
            'subcategory' => 'nullable|string|max:100',
            'remarks' => 'nullable|string',
            'photourl' => 'nullable|array',
            'files' => 'nullable|array',
            'owner' => 'nullable|string',
            'manager' => 'nullable|string',
            'managers' => 'nullable|array',
            'cooperatives' => 'nullable|array',
            'cooperatives.*' => 'string',
        ], [
            'name.unique' => 'A store with this name already exists. Please choose a different name.',
        ]);

        if ($validator->fails()) {
            return response()->json([
                'success' => false,
                'message' => $validator->errors()->first(),
                'errors' => $validator->errors()
            ], 422);
        }

        $validated = $validator->validated();

        $storeCode = $request->input('storecode')
            ?? self::generateStoreCode($request->input('category', 'General'));

        /** @var User $currentUser */
        $currentUser = Auth::user();
        if (!$currentUser) {
            return ResponseHelper::returnUnauthorized();
        }

        $acctTypeEarly = $currentUser->acct_type instanceof UserTypes ? $currentUser->acct_type : UserTypes::tryFrom($currentUser->acct_type);
        $isStoreOwner = $acctTypeEarly === UserTypes::STORE_OWNER;

        // STORE_OWNER: force owner to self, ignore any client-supplied owner/manager
        if ($isStoreOwner) {
            $ownerId = $currentUser->id;
            $managerId = $currentUser->id;
        } else {
            $ownerId = ($validated['owner'] ?? null) ? UserController::findUserIdByHash($validated['owner']) : null;
            $managerId = ($validated['manager'] ?? null) ? UserController::findUserIdByHash($validated['manager']) : null;
        }
 
        $acctType = $acctTypeEarly;
        $isBig3 = in_array($acctType, [UserTypes::ULTIMATE, UserTypes::SUPER_OPERATOR, UserTypes::OPERATOR]);

        // RBAC: only Big3 or STORE_OWNER may create stores
        if (!$isBig3 && !$isStoreOwner) {
            return response()->json(['success' => false, 'message' => 'You are not allowed to create stores.'], 403);
        }

        if (!$isBig3 && !$isStoreOwner && $ownerId) {
            $isParent = UserPermissions::IsParentofTargetUser($validated['owner']);
            if (!$isParent && Auth::id() !== $ownerId) {
                return response()->json(['success' => false, 'message' => 'Unauthorized to create store for this owner'], 403);
            }
        }

        // STORE_OWNER cannot override status — always active
        $status = $isStoreOwner ? 'active' : $request->input('status', 'active');

        $data = [
            'storecode' => $storeCode,
            'name' => $validated['name'],
            'description' => $validated['description'],
            'status' => $status,
            'remarks' => $isStoreOwner ? '' : ($validated['remarks'] ?? ''),
            'category' => $validated['category'] ?? 'General',
            'subcategory' => $validated['subcategory'] ?? '',
            'photourl' => $validated['photourl'] ?? $validated['files'] ?? '',
            'address' => $validated['address'],
            'owner_id' => $ownerId,
            'manager_id' => $managerId,
            'created_by' => Auth::id(),
            'is_active' => true,
        ];

        $store = Store::create($data);

        // STORE_OWNER auto-self-assigns as manager
        if ($isStoreOwner) {
            \App\Models\Market\StoreManager::create([
                'store_id'   => $store->id,
                'user_id'    => $currentUser->id,
                'created_by' => $currentUser->id,
            ]);
        }

        // Handle multiple managers. For STORE_OWNER, restrict to STORE_MANAGER descendants.
        if ($request->filled('managers') && is_array($request->input('managers'))) {
            $descendantIds = null;
            if ($isStoreOwner) {
                $descendantIds = $currentUser->getAllDescendants()->pluck('id')->toArray();
            }

            foreach ($request->input('managers') as $mgrHash) {
                $mgrId = UserController::findUserIdByHash($mgrHash);
                if (!$mgrId) {
                    continue;
                }

                if ($isStoreOwner) {
                    if (!in_array($mgrId, $descendantIds, true)) {
                        continue;
                    }
                    $mgrUser = User::find($mgrId);
                    $mgrType = $mgrUser?->acct_type instanceof UserTypes ? $mgrUser->acct_type : UserTypes::tryFrom($mgrUser?->acct_type ?? '');
                    if ($mgrType !== UserTypes::STORE_MANAGER) {
                        continue;
                    }
                    if ($mgrId === $currentUser->id) {
                        continue; // already added above
                    }
                }

                \App\Models\Market\StoreManager::create([
                    'store_id' => $store->id,
                    'user_id' => $mgrId,
                    'created_by' => Auth::id(),
                ]);
            }
        }

        // Sync cooperative links (optional, many-to-many) — admins only
        if (!$isStoreOwner) {
            $this->syncStoreCooperatives($store, $request->input('cooperatives', []));
        }

        return Response::json(['success' => true, 'hashkey' => $store->hashkey], 201);
    }

    /**
     * One-click store creation: assigns the current user as both owner
     * and sole manager, with sensible defaults. Used by the Store Owner home.
     */
    public function autoCreate(Request $request)
    {
        /** @var User $currentUser */
        $currentUser = Auth::user();
        if (!$currentUser) {
            return ResponseHelper::returnUnauthorized();
        }

        $acctType = $currentUser->acct_type instanceof UserTypes
            ? $currentUser->acct_type
            : UserTypes::tryFrom($currentUser->acct_type);
        $isBig3 = in_array($acctType, [UserTypes::ULTIMATE, UserTypes::SUPER_OPERATOR, UserTypes::OPERATOR]);
        $isStoreOwner = $acctType === UserTypes::STORE_OWNER;

        if (!$isBig3 && !$isStoreOwner) {
            return response()->json(['success' => false, 'message' => 'You are not allowed to create stores.'], 403);
        }

        $displayName = $currentUser->nickname ?: ($currentUser->name ?: 'My');
        $name = trim((string) $request->input('name')) ?: ($displayName . "'s Store");
        $description = trim((string) $request->input('description')) ?: 'Auto-created store';
        $address = trim((string) $request->input('address')) ?: 'Address not set';
        $category = $request->input('category', 'General');

        // `str.name` is globally unique; if the auto-generated name collides
        // (two owners sharing a nickname), append a short suffix until free.
        $baseName = $name;
        $attempt = 0;
        while (Store::where('name', $name)->exists()) {
            $attempt++;
            if ($attempt > 20) {
                return response()->json([
                    'success' => false,
                    'message' => 'Could not generate a unique store name. Please use Custom Create.',
                ], 409);
            }
            $name = $baseName . ' #' . strtoupper(substr(bin2hex(random_bytes(2)), 0, 4));
        }

        $store = Store::create([
            'storecode'   => self::generateStoreCode($category),
            'name'        => $name,
            'description' => $description,
            'status'      => 'active',
            'remarks'     => '',
            'category'    => $category,
            'subcategory' => '',
            'photourl'    => '',
            'address'     => $address,
            'owner_id'    => $currentUser->id,
            'manager_id'  => $currentUser->id,
            'created_by'  => $currentUser->id,
            'is_active'   => true,
        ]);

        \App\Models\Market\StoreManager::create([
            'store_id'   => $store->id,
            'user_id'    => $currentUser->id,
            'created_by' => $currentUser->id,
        ]);

        return Response::json([
            'success' => true,
            'hashkey' => $store->hashkey,
            'name'    => $store->name,
        ], 201);
    }

    public static function generateStoreCode(string $category, ?int $num = null, int $length = 8): string
    {
        $characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_';
        $charactersLength = strlen($characters);


        $num = $num ?? rand(1, 99);


        $randomString = '';
        for ($i = 0; $i < $length; $i++) {
            $randomString .= $characters[rand(0, $charactersLength - 1)];
        }

        $categoryPrefix = substr($category, 0, 2);
        $categorySuffix = substr($category, -2, 2);
        $cat = strtoupper($categoryPrefix . $categorySuffix);

        return date('Y') . '-' . $cat . '-' . $num . '-' . $randomString;
    }

    public static function viewStoreDetailsLegacy(Request|false $request = false, string|false $target = false)
    {
        try {
            if ($target) {
                $hash = $target;
            } else {
                $hash = $request->input('target');
            }
            if (!$hash) {
                return $target ? false : response()->json(['success' => false]);
            }
 
            $store = Store::where('hashkey', $hash)->first();

            if (!$store) {
                return $target ? false : response()->json(['success' => false]);
            }

            $products = Product::where('store_id', $store->id)
                ->select('name', 'hashkey', 'price', 'unitname', 'available', 'is_active as status', 'photourl')
                ->get()
                ->filter(fn($product) => $product->available)
                ->map(function ($product) {
                    $photourl = $product->photourl;
                    if ($photourl && (is_array($photourl) || is_object($photourl))) {
                        $product->photourl = (array)$photourl;
                    } elseif (is_string($photourl)) {
                        $product->photourl = json_decode($photourl, true) ?: [];
                    } else {
                        $product->photourl = [];
                    }
                    return $product;
                })
                ->values();

            $store->products = $products;

            if ($target) {
                return $store;
            } else {
                return response()->json(['success' => true, 'data' => $store]);
            }
        } catch (\Exception $e) {
            return response()->json(['success' => false, 'message' => $e->getMessage()], 500);
        }
    }

    public static function viewStoreDetails(Request|false $request = false, string|false $target = false)
    {

        $req = $request ?: request();

        $hash = $target ?: ($req?->input('target') ?? null);

        if (!$hash) {
            return $target ? false : response()->json(['success' => false]);
        }

        // Fetch the store by hashkey
        $store = Store::where('hashkey', $hash)
            ->select(
                'id',
                'photourl',
                'hashkey',
                'name',
                'category',
                'subcategory',
                'created_at as created',
                'updated_at as modified',
                'description',
                'is_active as status',
                'address',
                'owner_id',
                'manager_id'
            )
            ->first();

        if (!$store) {
            return $target ? false : response()->json(['success' => false]);
        }



        /** @var User $user */
        $user = Auth::user();
        $products = $store->products()
            ->select(

                'prd_items.name',
                'prd_items.hashkey',
                'prd_str.price as store_price',
                'prd_items.price',
                'prd_items.unitname',
                'prd_str.available',
                'prd_str.is_active as status',
                'prd_items.photourl'
            )
            ->get()
            //TODO Decide later wheether to include unavailable store products
            // ->filter(fn($product) => $product->available)
            ->map(function ($product) {
                $photourl = $product->photourl;
                if ($photourl && is_array($photourl)) {
                    $product->photourl = $photourl;
                } elseif (is_string($photourl)) {
                    $product->photourl = json_decode($photourl, true) ?: [];
                } else {
                    $product->photourl = [];
                }

                return $product;
            })
            ->values();

        $store->products = $products;

        // Resolve store photo URLs: prefer cdn_url, fall back to /RequestData/File/{hash}
        $rawPhotos = $store->photourl ?? [];
        if (!empty($rawPhotos) && is_array($rawPhotos)) {
            $store->resolved_photos = \App\Models\FileList::whereIn('hashkey', $rawPhotos)
                ->get(['hashkey', 'cdn_url'])
                ->keyBy('hashkey')
                ->pipe(function ($fileMap) use ($rawPhotos) {
                    return collect($rawPhotos)->map(function ($hash) use ($fileMap) {
                        $file = $fileMap->get($hash);
                        if (!$file) return null;
                        $cdn = trim((string)($file->cdn_url ?? ''));
                        return $cdn !== '' ? $cdn : "/RequestData/File/{$hash}";
                    })->filter()->values()->toArray();
                });
        } else {
            $store->resolved_photos = [];
        }

        // Check if user can add products to this store
        $store->can_add_product = ProductPermissions::isActionAllowed(UserActions::AddProducttoOwnStore, null, $store)
            || ProductPermissions::isActionAllowed(UserActions::AddProducttoAnyStore, null, $store);

        // Check if user can assign existing products to this store
        $store->can_assign_product = self::canUserAssignProduct($store);

        // Check if user can access POS for this store
        $store->can_access_pos = self::canUserAccessPos($store);

        // Return the formatted response
        return $target ? $store : response()->json(['success' => true, 'data' => $store]);
    }

    /**
     * Determine if the current user can assign existing products to a given store.
     * Mirrors the permission check in ProductController::AssignProductToOwnStore.
     */
    private static function canUserAssignProduct(Store $store): bool
    {
        /** @var User $user */
        $user = Auth::user();
        if (!$user) {
            return false;
        }

        $acctType = $user->acct_type->value ?? $user->acct_type ?? '';

        if (in_array($acctType, ['ult', 'super operator', 'operator'])) {
            return true;
        }

        if ($store->owner_id === $user->id || $store->manager_id === $user->id) {
            return true;
        }

        if ($store->managers()->where('user_id', $user->id)->exists()) {
            return true;
        }

        if (UserPermissions::isDescendantOfCurrentUser($store->owner_id)) {
            return true;
        }

        $managerIds = $store->managerUsers()->pluck('users.id')->toArray();
        foreach ($managerIds as $managerId) {
            if (UserPermissions::isDescendantOfCurrentUser($managerId)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Determine if the current user can access POS for a given store.
     * Returns true if:
     * - User is the store owner or manager
     * - User is a direct/indirect parent of the store owner (via parentuid chain)
     * - User has an admin role (ult, super operator, operator)
     */
    private static function canUserAccessPos(Store $store): bool
    {
        /** @var User $user */
        $user = Auth::user();
        if (!$user) {
            return false;
        }

        $acctType = $user->acct_type->value ?? $user->acct_type ?? '';

        // Big 3 always have access
        if (in_array($acctType, ['ult', 'super operator', 'operator'])) {
            return true;
        }

        // Store owner
        if ($store->owner_id === $user->id) {
            return true;
        }

        // Check if user is a store manager (primary or in the list)
        if ($store->manager_id === $user->id) {
            return true;
        }

        if ($store->managers()->where('user_id', $user->id)->exists()) {
            return true;
        }

        // Check hierarchy: parent of owner or any manager
        if (UserPermissions::isDescendantOfCurrentUser($store->owner_id)) {
            return true;
        }

        $managerIds = $store->managerUsers()->pluck('users.id')->toArray();
        foreach ($managerIds as $managerId) {
            if (UserPermissions::isDescendantOfCurrentUser($managerId)) {
                return true;
            }
        }

        return false;
    }



    public static function editStoreDetails(Request|false $request = false, string|false $target = false)
    {

        $req = $request ?: request();

        $hash = $target ?: ($req?->input('target') ?? null);

        // file_put_contents('Storedetails.txt',$hash);
        if (!$hash) {
            return $target ? false : response()->json(['success' => false]);
        }

        $store = Store::where('hashkey', $hash)
            ->select(
                'photourl',
                'hashkey',
                'name',
                'category',
                'subcategory',
                'created_at as created',
                'updated_at as modified',
                'description',
                'photourl',
                'status',
                'is_active',
                'remarks',
                'address',
                'owner_id',
                'manager_id',
            )->with('owner:hashkey,id')
            ->with('manager:hashkey,id')
            ->first();

        if (!$store) {
            return $target ? false : response()->json(['success' => false]);
        }

        try {
            $store->owner_hashkey = $store->owner->hashkey;
        } catch (\Exception $th) {
            $store->owner_hashkey = null;
        }

        try {
            $store->manager_hashkey = $store->manager->hashkey;
        } catch (\Throwable $th) {
            $store->manager_hashkey = 0;
        }

        try {
            $store->photourlDropzone = PhotoURL::photoURLArraytoDropzoneData($store->photourl);
        } catch (\Throwable $th) {
            $store->photourlDropzone = [];
        }

        try {
            $store->ParentList = CreateUserControllerUltimate::listAllUsersforParentSelectHTML(request(), true);
        } catch (\Exception $e) {
            $store->ParentList = [];
        }

        // Include multi-manager hashkeys for frontend
        try {
            $store->managers_hashkeys = $store->managerUsers()->pluck('users.hashkey')->toArray();
        } catch (\Throwable $th) {
            $store->managers_hashkeys = [];
        }

        // Include linked cooperative hashkeys
        try {
            $store->cooperative_hashkeys = $store->cooperatives()->pluck('organizations.hashkey')->toArray();
        } catch (\Throwable $th) {
            $store->cooperative_hashkeys = [];
        }

        $products = $store->products()
            ->select(
                'prd_items.name',
                'prd_items.hashkey',
                'prd_str.price',
                'prd_items.unitname',
                'prd_str.available',
                'prd_str.is_active as status',
                'prd_items.photourl'
            )
            ->get()
            ->filter(fn($product) => $product->available)
            ->map(function ($product) {
                $photourl = $product->photourl;
                if ($photourl && is_array($photourl)) {
                    $product->photourl = $photourl;
                } elseif (is_string($photourl)) {
                    $product->photourl = json_decode($photourl, true) ?: [];
                } else {
                    $product->photourl = [];
                }
                return $product;
            })
            ->values();

        return $target ? $store : response()->json($store);
    }

    public function update(Request $request)
    {
        $hashkey = request()->input('target');
        $store = Store::where('hashkey', $hashkey)->first();

        if (!$store) {
            return response()->json([
                'success' => false,
                'message' => 'Store not found.'
            ], 404);
        }

        /** @var User $user */
        $user = Auth::user();
        if (!$user) {
            return response()->json(['error' => 'Unauthorized'], 401);
        }

        $acctType = $user->acct_type instanceof UserTypes ? $user->acct_type : UserTypes::tryFrom($user->acct_type);
        $isBig3 = in_array($acctType, [UserTypes::ULTIMATE, UserTypes::SUPER_OPERATOR, UserTypes::OPERATOR]);

        // Permission check: Big 3, parent of owner, direct owner, direct manager, or pivot manager
        $isParentOfOwner = UserPermissions::isDescendantOfCurrentUser($store->owner_id);
        $isDirectManager = $store->manager_id === $user->id;
        $isManagerViaPivot = $store->managers()->where('user_id', $user->id)->exists();
        if (!$isBig3 && !$isParentOfOwner && $store->owner_id !== $user->id && !$isDirectManager && !$isManagerViaPivot) {
             return response()->json(['error' => 'Unauthorized to modify this store'], 403);
        }

        $storeId = $store->id;
        $validator = Validator::make($request->all(), [
            'name' => [
                'required', 'string', 'max:255',
                function ($attribute, $value, $fail) use ($storeId) {
                    $conflict = \App\Models\Market\Store::where('name', $value)
                        ->where('id', '!=', $storeId)
                        ->exists();
                    if ($conflict) {
                        $fail('A store with this name already exists. Please choose a different name.');
                    }
                },
            ],
            'description' => 'required|string',
            'address' => 'required|string',
            'category' => 'nullable|string|max:100',
            'subcategory' => 'nullable|string|max:100',
            'remarks' => 'nullable|string',
            'photourl' => 'nullable|array',
            'files' => 'nullable|array',
            'owner' => 'nullable|string',
            'manager' => 'nullable|string',
            'managers' => 'nullable|array',
            'cooperatives' => 'nullable|array',
            'cooperatives.*' => 'string',
        ]);

        if ($validator->fails()) {
            return response()->json([
                'success' => false,
                'errors' => $validator->errors()
            ], 422);
        }

        $validated = $validator->validated();

        $ownerId = ($validated['owner'] ?? null) ? UserController::findUserIdByHash($validated['owner']) : $store->owner_id;
        $managerId = ($validated['manager'] ?? null) ? UserController::findUserIdByHash($validated['manager']) : $store->manager_id;

        $status = $request->input('status', $store->status ?? 'active');

        $data = [
            'name' => $validated['name'],
            'description' => $validated['description'],
            'address' => $validated['address'],
            'remarks' => $validated['remarks'] ?? $store->remarks,
            'category' => $validated['category'] ?? $store->category,
            'subcategory' => $validated['subcategory'] ?? $store->subcategory,
            'status' => $status,
            'photourl' => $validated['photourl'] ?? $validated['files'] ?? $store->photourl,
            'owner_id' => $ownerId,
            'manager_id' => $managerId,
            'updated_by' => Auth::id(),
        ];

        $store->update($data);

        // Handle multiple managers
        if ($request->filled('managers') && is_array($request->input('managers'))) {
            // Remove existing managers not in the new list
            $newMgrIds = [];
            foreach ($request->input('managers') as $mgrHash) {
                $mgrId = UserController::findUserIdByHash($mgrHash);
                if ($mgrId) $newMgrIds[] = $mgrId;
            }
            
            $store->managers()->whereNotIn('user_id', $newMgrIds)->delete();

            // Add new ones
            foreach ($newMgrIds as $mgrId) {
                if (!$store->managers()->where('user_id', $mgrId)->exists()) {
                    \App\Models\Market\StoreManager::create([
                        'store_id' => $store->id,
                        'user_id' => $mgrId,
                        'created_by' => Auth::id(),
                    ]);
                }
            }
        }

        // Sync cooperative links (only when key is present, allows clearing with empty array)
        if ($request->has('cooperatives')) {
            $this->syncStoreCooperatives($store, $request->input('cooperatives', []), true);
        }

        return response()->json([
            'success' => true,
            'message' => 'Store updated successfully.',
            'hashkey' => $store->hashkey,
        ], 200);
    }


    /**
     * Attach a store to a product with optional pivot data.
     *
     * This creates (or updates) a relationship between a given Product and Store
     * in the `product_store` pivot table. It allows specifying stock quantity,
     * price, and active status for that store-product link.
     *
     * If either the product or store cannot be found, the method safely returns false.
     *
     * @param  \App\Models\Market\Product|int  $product  The Product model instance or its ID.
     * @param  \App\Models\Market\Store|int  $store     The Store model instance or its ID.
     * @param  int  $available  Quantity available for this product in the store (default: 0)
     * @param  int  $price  Price for this product in the store (default: 0)
     * @param  bool  $is_active  Whether the product is active in this store (default: true)
     * @return bool  Returns true on success, false if product/store not found or an exception occurs.
     *
     * @example
     * ```php
     * // Attach store #5 to product #12 with stock and price
     * Product::attachStoreToProduct(12, 5, available: 40, price: 1499, is_active: true);
     *
     * // Using model instances
     * $product = Product::find(12);
     * $store = Store::find(5);
     * Product::attachStoreToProduct($product, $store, 25, 1299);
     * ```
     */
    public static function attachStoreToProduct(Product|int $product, Store|int $store, int $available = 0, int $price = 0, bool $is_active = true)
    {

        if (is_int($product)) {
            try {
                $product = Product::findOrFail($product);
            } catch (\Throwable $th) {
                return false;
            }
        }


        if (is_int($store)) {
            try {
                $store = Store::findOrFail($store);
            } catch (\Throwable $th) {
                return false;
            }
        }


        try {
            $product->stores()->syncWithoutDetaching([
                $store->id => [
                    'available' => $available ?? 0,
                    'price' => $price ?? 0,
                    'is_active' => $is_active,
                ]
            ]);
            return true;
        } catch (\Throwable $th) {
            return false;
        }
    }

    /**
     * Attach a product to a store with optional pivot data.
     *
     * This creates (or updates) a relationship between a given Store and Product
     * in the `product_store` pivot table. You can specify availability, price,
     * and activation status as pivot attributes.
     *
     * If either the store or product is not found, the method safely returns false.
     *
     * @param  \App\Models\Market\Store|int  $store   The Store model instance or its ID.
     * @param  \App\Models\Market\Product|int  $product  The Product model instance or its ID.
     * @param  int  $available  Quantity available (default: 0)
     * @param  int  $price  Product price (default: 0)
     * @param  bool  $is_active  Whether the product is active in this store (default: true)
     * @return bool  Returns true on success, false if store/product not found or an exception occurs.
     *
     * @example
     * ```php
     * // Attach product #10 to store #3
     * Store::attachProductToStore(3, 10, 25, 1999, true);
     *
     * // Using model instances
     * $store = Store::find(3);
     * $product = Product::find(10);
     * Store::attachProductToStore($store, $product, available: 100, price: 2499);
     * ```
     */
    public static function attachProducttoStore(Store|int $store, Product|int $product, int $available = 0, int $price = 0, bool $is_active = true)
    {
        if (is_int($store)) {
            try {
                $store = Store::findOrFail($store);
            } catch (\Throwable $th) {
                return false;
            }
        }
        if (is_int($product)) {
            try {
                $product = Product::findOrFail($product);
            } catch (\Throwable $th) {
                return false;
            }
        }

        $store->products()->attach($product->id, ['available' => $available ?? 0, 'price' => $price ?? 0, 'is_active' => $is_active,]);
        return true;

    }


    /**
     * Detach (unlink) a product from a store.
     *
     * This removes the relationship between a given Store and Product
     * from the `product_store` pivot table, without deleting either record.
     *
     * If the link does not exist, this method silently succeeds (no error is thrown).
     * If either the store or product cannot be found, the method safely returns false.
     *
     * @param  \App\Models\Market\Store|int  $store   The Store model instance or its ID.
     * @param  \App\Models\Market\Product|int  $product  The Product model instance or its ID.
     * @return bool  Returns true on success, false if store/product not found or an exception occurs.
     *
     * @example
     * ```php
     * // Detach product #10 from store #3
     * Store::detachProductFromStore(3, 10);
     *
     * // Using model instances
     * $store = Store::find(3);
     * $product = Product::find(10);
     * Store::detachProductFromStore($store, $product);
     * ```
     */
    public static function detachProductFromStore(Store|int $store, Product|int $product)
    {
        // Resolve Store
        if (is_int($store)) {
            try {
                $store = Store::findOrFail($store);
            } catch (\Throwable $th) {
                return false;
            }
        }

        // Resolve Product
        if (is_int($product)) {
            try {
                $product = Product::findOrFail($product);
            } catch (\Throwable $th) {
                return false;
            }
        }

        // Attempt to detach
        try {
            $store->products()->detach($product->id);
            return true;
        } catch (\Throwable $th) {
            return false;
        }
    }




    /**
     * Detach (unlink) a store from a product.
     *
     * This removes the relationship between a given Product and Store
     * from the `product_store` pivot table, without deleting either record.
     *
     * If the relationship doesn’t exist, the operation succeeds silently.
     * If either the product or store is not found, the method safely returns false.
     *
     * @param  \App\Models\Market\Product|int  $product  The Product model instance or its ID.
     * @param  \App\Models\Market\Store|int    $store    The Store model instance or its ID.
     * @return bool  Returns true on success, false if product/store not found or an exception occurs.
     *
     * @example
     * ```php
     * // Detach store #2 from product #7
     * Product::detachStoreFromProduct(7, 2);
     *
     * // Using model instances
     * $product = Product::find(7);
     * $store = Store::find(2);
     * Product::detachStoreFromProduct($product, $store);
     * ```
     */
    public static function detachStoreFromProduct(Product|int|string $product, Store|int|string $store)
    {
        if (is_int($product)) {
            try {
                $product = Product::findOrFail($product);
            } catch (\Throwable $th) {
                $product = null;
            }
        }

        if (is_int($store)) {
            try {
                $store = Store::findOrFail($store);
            } catch (\Throwable $th) {
                $store = null;
            }
        }

        if (is_string($product)) {
            try {
                $product = Product::where('hashkey', $product)->first();
            } catch (\Throwable $th) {
                $store = null;
            }
        }

        if (is_string($store)) {
            try {
                $store = Store::where('hashkey', $store)->first();
            } catch (\Throwable $th) {
                $store = null;
            }
        }

        if (!$store || !$product) {
            return false;
        }

        // Attempt to detach
        try {
            $product->stores()->detach($store->id);
            return true;
        } catch (\Throwable $th) {
            return false;
        }
    }



    /**
     * Synchronize (replace) all products attached to a given store.
     *
     * This method accepts either a Store model instance or a store ID.
     * It replaces the store's entire product list in the `product_store` pivot table
     * with the provided data array.
     *
     * Existing product links not included in the `$products` array will be detached.
     * New links will be created, and existing ones will be updated.
     *
     * @param  \App\Models\Market\Store|int  $store     The Store model instance or its ID.
     * @param  array  $products                  Associative array of product IDs and pivot attributes.
     *                                           Format:
     *                                           [
     *                                               product_id => [
     *                                                   'available' => (int),
     *                                                   'price' => (int),
     *                                                   'is_active' => (bool),
     *                                               ],
     *                                               ...
     *                                           ]
     * @return bool  Returns true on success, false if the store is not found or an exception occurs.
     *
     * @example
     * ```php
     * // Replace all products for store #1
     * Store::syncProductsToStore(1, [
     *     10 => ['available' => 50, 'price' => 1499, 'is_active' => true],
     *     11 => ['available' => 20, 'price' => 1999, 'is_active' => false],
     * ]);
     *
     * // Using a Store instance
     * $store = Store::find(1);
     * Store::syncProductsToStore($store, [
     *     5 => ['available' => 100, 'price' => 2999, 'is_active' => true],
     * ]);
     * ```
     */
    public static function syncProductsToStore(Store|int $store, array $products)
    {
        // $products format: [product_id => ['available' => ..., 'price' => ..., 'is_active' => ...]]

        // Resolve store
        if (is_int($store)) {
            try {
                $store = Store::findOrFail($store);
            } catch (\Throwable $th) {
                return false;
            }
        }

        // Validate array structure (optional)
        if (!is_array($products) || empty($products)) {
            return false;
        }

        try {
            // Replace all existing product links
            $store->products()->sync($products);
            return true;
        } catch (\Throwable $th) {
            return false;
        }
    }



    /**
     * Synchronize (replace) all stores attached to a given product.
     *
     * This method accepts either a Product model instance or a product ID.
     * It replaces the product's entire store list in the `product_store` pivot table
     * with the provided data array.
     *
     * Existing store links not listed in the `$stores` array will be detached.
     * New ones will be created, and existing ones will be updated.
     *
     * @param  \App\Models\Market\Product|int  $product  The Product model instance or its ID.
     * @param  array  $stores                     Associative array of store IDs and pivot attributes.
     *                                            Format:
     *                                            [
     *                                                store_id => [
     *                                                    'available' => (int),
     *                                                    'price' => (int),
     *                                                    'is_active' => (bool),
     *                                                ],
     *                                                ...
     *                                            ]
     * @return bool  Returns true on success, false if the product is not found or an exception occurs.
     *
     * @example
     * ```php
     * // Replace all stores linked to product #5
     * Product::syncStoresToProduct(5, [
     *     1 => ['available' => 30, 'price' => 1799, 'is_active' => true],
     *     2 => ['available' => 15, 'price' => 1999, 'is_active' => false],
     * ]);
     *
     * // Using a Product instance
     * $product = Product::find(5);
     * Product::syncStoresToProduct($product, [
     *     3 => ['available' => 40, 'price' => 1499, 'is_active' => true],
     * ]);
     * ```
     */
    public static function syncStoresToProduct(Product|int $product, array $stores)
    {
        // $stores format: [store_id => ['available' => ..., 'price' => ..., 'is_active' => ...]]

        // Resolve product
        if (is_int($product)) {
            try {
                $product = Product::findOrFail($product);
            } catch (\Throwable $th) {
                return false;
            }
        }

        if (!is_array($stores) || empty($stores)) {
            return false;
        }

        try {
            // Replace all existing store links
            $product->stores()->sync($stores);
            return true;
        } catch (\Throwable $th) {
            return false;
        }
    }

    public function listStoresActiveDataAll()
    {
        $stores = Store::where('status', 'active')
            ->select('hashkey', 'photourl', 'name', 'category', 'subcategory')
            ->get()
            ->map(function ($store) {
                if (is_array($store->photourl)) {
                    $photoUrl = $store->photourl;
                } elseif (is_string($store->photourl)) {
                    $photoUrl = json_decode($store->photourl, true) ?: [];
                } else {
                    $photoUrl = [];
                }

                if (is_array($photoUrl) && !empty($photoUrl)) {
                    $photoUrl = $photoUrl[0];
                } else {
                    $photoUrl = null;
                }


                $store->photourl = $photoUrl;

                return $store;
            });

        return $stores;
    }

    /**
     * List stores the current user is authorized to manage.
     * - Ultimate users: all stores
     * - Store owners: stores they own
     * - Store managers: stores they manage
     * Returns a JSON array of stores with hashkey, name, and role.
     */
    public function listStoresForCurrentUser()
    {
        $user = Auth::user();

        if (!$user) {
            return Response::json([]);
        }

        $isUltimate = $user->acct_type === UserTypes::ULTIMATE;
        $isSuperOperator = $user->acct_type === UserTypes::SUPER_OPERATOR;
        $isOperator = $user->acct_type === UserTypes::OPERATOR;

        if ($isUltimate) {
            // Ultimate can see all active stores
            $stores = Store::where('is_active', true)
                ->select(['hashkey', 'name', 'category', 'owner_id', 'manager_id'])
                ->get();
        } elseif ($isSuperOperator || $isOperator) {
            // Super Operator and Operator see their own and descendants' stores
            $descendants = $user->getAllDescendants();
            $allowedIds = $descendants->pluck('id')->push($user->id)->toArray();

            $stores = Store::where('is_active', true)
                ->where(function($query) use ($allowedIds) {
                    $query->whereIn('owner_id', $allowedIds)
                          ->orWhereIn('manager_id', $allowedIds);
                })
                ->select(['hashkey', 'name', 'category', 'owner_id', 'manager_id'])
                ->get();
        } else {
            // Non-admin users: only stores they own or manage (including store_managers pivot)
            $stores = Store::where('is_active', true)
                ->where(function($query) use ($user) {
                    $query->where('owner_id', $user->id)
                          ->orWhere('manager_id', $user->id)
                          ->orWhereHas('managers', function ($mq) use ($user) {
                              $mq->where('user_id', $user->id);
                          });
                })
                ->select(['hashkey', 'name', 'category', 'owner_id', 'manager_id'])
                ->get();
        }

        $formattedStores = $stores->map(function ($store) use ($user) {
            $role = 'viewer';
            if ($store->owner_id === $user->id) {
                $role = 'owner';
            } elseif ($store->manager_id === $user->id) {
                $role = 'manager';
            } elseif ($store->managers()->where('user_id', $user->id)->exists()) {
                $role = 'manager';
            } elseif ($user->isUltimate() || $user->isSuperOperator() || $user->isOperator()) {
                $role = 'admin';
            }
            return [
                'hashkey' => $store->hashkey,
                'name' => $store->name,
                'category' => $store->category,
                'role' => $role,
            ];
        });

        return Response::json($formattedStores);
    }

    public function removeProductfromStore()
    {
        //TODO fix permissions here

        $product = request()->input('product_hash');
        $store = request()->input('store_hash');



        $isUlt = Auth::user()->acct_type === UserTypes::ULTIMATE;

        if ($isUlt) {
            $allowed = ProductPermissions::isActionAllowed(UserActions::AddProducttoAnyStore, $product, $store);
        } else {
            $allowed = ProductPermissions::isActionAllowed(UserActions::AddProducttoOwnStore, $product, $store);
        }


        if (!$allowed) {
            return ResponseHelper::returnUnauthorized();
        }



        $go = self::detachStoreFromProduct($product, $store);

        if ($go === true) {
            $datareturn = [
                'store_hash' => $store,
                'product_hash' => $product,
            ];
            return ResponseHelper::returnSuccessResponse($datareturn, $product, '');
        } else {
            return ResponseHelper::returnError($go);
        }
    }


    public function listSelectableStoresForAddingProduct()
    {
        /** @var \App\Models\User $user */
        $user = Auth::user();
        if (!$user) {
            return ResponseHelper::returnUnauthorized();
        }

        $acctType = $user->acct_type instanceof UserTypes ? $user->acct_type : UserTypes::tryFrom($user->acct_type);
        $isBig3 = in_array($acctType, [UserTypes::ULTIMATE, UserTypes::SUPER_OPERATOR, UserTypes::OPERATOR]);

        if ($isBig3) {
            // Big 3 can see all stores
            $stores = Store::select(['hashkey', 'name', 'category', 'owner_id', 'manager_id'])
                ->get();
        } else {
            // Include self + all descendants so a store owner sees their own stores.
            $allowedUserIds = array_merge([$user->id], $user->getAllDescendants()->pluck('id')->toArray());

            $stores = Store::where(function ($q) use ($allowedUserIds) {
                    $q->whereIn('owner_id', $allowedUserIds)
                      ->orWhereIn('manager_id', $allowedUserIds)
                      ->orWhereHas('managers', function ($mq) use ($allowedUserIds) {
                          $mq->whereIn('user_id', $allowedUserIds);
                      });
                })
                ->select(['hashkey', 'name', 'category', 'owner_id', 'manager_id'])
                ->get();
        }

        $formattedStores = $stores->map(function ($store) use ($user) {
            $role = 'admin';
            if ($store->owner_id === $user->id) {
                $role = 'owner';
            } elseif ($store->manager_id === $user->id) {
                $role = 'manager';
            }
            return [
                'hashkey' => $store->hashkey,
                'name' => $store->name,
                'category' => $store->category,
                'role' => $role,
            ];
        });

        return response()->json([
            'success' => true,
            'data' => $formattedStores,
        ]);
    }

    public function listStores_Admin(Request $request)
    {
        try {
            $user = Auth::user();
            if (!$user) {
                return ResponseHelper::returnUnauthorized();
            }

            $acctType = $user->acct_type instanceof UserTypes ? $user->acct_type : UserTypes::tryFrom($user->acct_type);
            $isBig3 = in_array($acctType, [UserTypes::ULTIMATE, UserTypes::SUPER_OPERATOR, UserTypes::OPERATOR]);

            $columns = ['id', 'hashkey', 'name', 'category', 'subcategory', 'is_active', 'status', 'photourl', 'address', 'owner_id', 'manager_id'];

            if ($isBig3) {
                $stores = Store::with(['cooperatives:organizations.id,organizations.hashkey,organizations.name'])
                    ->select($columns)
                    ->orderBy('id', 'desc')
                    ->get()
                    ->each(fn($s) => $s->setAttribute('user_can_manage', true));
            } else {
                $allowedUserIds = array_merge([$user->id], $user->getAllDescendants()->pluck('id')->toArray());

                $stores = Store::with(['cooperatives:organizations.id,organizations.hashkey,organizations.name', 'managers:id,store_id,user_id'])
                    ->select($columns)
                    ->where(function ($q) use ($allowedUserIds) {
                        $q->whereIn('owner_id', $allowedUserIds)
                          ->orWhereIn('manager_id', $allowedUserIds)
                          ->orWhereHas('managers', function ($mq) use ($allowedUserIds) {
                              $mq->whereIn('user_id', $allowedUserIds);
                          });
                    })
                    ->orderBy('id', 'desc')
                    ->get()
                    ->each(function ($s) use ($allowedUserIds) {
                        $s->setAttribute('user_can_manage',
                            in_array($s->owner_id, $allowedUserIds)
                            || in_array($s->manager_id, $allowedUserIds)
                            || $s->managers->pluck('user_id')->intersect($allowedUserIds)->isNotEmpty()
                        );
                        unset($s->managers);
                    });
            }

            return response()->json([
                'success' => true,
                'stores' => $stores
            ]);
        } catch (\Throwable $e) {
            \Hypervel\Support\Facades\Log::error('listStores_Admin failed', [
                'user_id' => Auth::id(),
                'message' => $e->getMessage(),
                'file' => $e->getFile() . ':' . $e->getLine(),
            ]);
            return response()->json([
                'success' => false,
                'message' => 'Failed to load stores: ' . $e->getMessage(),
            ], 500);
        }
    }

    /**
     * Resolve cooperative hashkeys to ids (filtering to type=COOPERATIVE)
     * and sync onto the store. When $forceReplace is false and the input
     * array is empty, this is a no-op (used on create).
     */
    private function syncStoreCooperatives(Store $store, $coopHashes, bool $forceReplace = false): void
    {
        if (!is_array($coopHashes)) {
            $coopHashes = [];
        }

        if (!$forceReplace && empty($coopHashes)) {
            return;
        }

        $coopIds = empty($coopHashes)
            ? []
            : Organization::whereIn('hashkey', $coopHashes)
                ->where('type', 'COOPERATIVE')
                ->pluck('id')
                ->toArray();

        $store->cooperatives()->sync($coopIds);
    }

    /**
     * Lightweight cooperative list for the store create/edit pickers.
     * Returns only active organizations of type COOPERATIVE.
     */
    public function listCooperativesForStore(Request $request)
    {
        $user = Auth::user();
        if (!$user) {
            return ResponseHelper::returnUnauthorized();
        }

        $coops = Organization::where('is_active', true)
            ->where('type', 'COOPERATIVE')
            ->select(['hashkey', 'name', 'cooperative_type', 'address'])
            ->orderBy('name')
            ->get();

        return response()->json([
            'success' => true,
            'data' => $coops,
        ]);
    }

    public function deleteStore_Admin(Request $request)
    {
        $user = Auth::user();
        if (!$user) {
            return ResponseHelper::returnUnauthorized();
        }

        $hash = $request->input('target');
        if (!$hash) {
            return ResponseHelper::returnIncorrectDetails();
        }

        try {
            $store = Store::where('hashkey', $hash)->firstOrFail();
            
            // Check permissions: Ultimate or Owner/Manager (or descendant thereof, or pivot manager)
            $isUltimate = $user->acct_type === UserTypes::ULTIMATE;
            if (!$isUltimate) {
                $descendants = $user->getAllDescendants();
                $descendantIds = $descendants->pluck('id')->toArray();
                $allowedIds = array_merge([$user->id], $descendantIds);
                $isManagerViaPivot = $store->managers()->whereIn('user_id', $allowedIds)->exists();
                if (!in_array($store->owner_id, $allowedIds) && !in_array($store->manager_id, $allowedIds) && !$isManagerViaPivot) {
                    return ResponseHelper::returnUnauthorized();
                }
            }

            $store->delete();
            return ResponseHelper::returnSuccessResponse([], $hash, 'Store deleted successfully');
        } catch (\Exception $e) {
            return ResponseHelper::returnError('Failed to delete store: ' . $e->getMessage());
        }
    }

    public function toggleStoreStatus_Admin(Request $request)
    {
        $user = Auth::user();
        if (!$user) {
            return ResponseHelper::returnUnauthorized();
        }

        $hash = $request->input('target');
        if (!$hash) {
            return ResponseHelper::returnIncorrectDetails();
        }

        try {
            $store = Store::where('hashkey', $hash)->firstOrFail();

            // Check permissions: Ultimate or Owner/Manager (or descendant thereof, or pivot manager)
            $isUltimate = $user->acct_type === UserTypes::ULTIMATE;
            if (!$isUltimate) {
                $descendants = $user->getAllDescendants();
                $descendantIds = $descendants->pluck('id')->toArray();
                $allowedIds = array_merge([$user->id], $descendantIds);
                $isManagerViaPivot = $store->managers()->whereIn('user_id', $allowedIds)->exists();
                if (!in_array($store->owner_id, $allowedIds) && !in_array($store->manager_id, $allowedIds) && !$isManagerViaPivot) {
                    return ResponseHelper::returnUnauthorized();
                }
            }

            $store->is_active = !$store->is_active;
            $store->save();

            return ResponseHelper::returnSuccessResponse(
                ['is_active' => $store->is_active],
                $hash,
                'Store status updated successfully'
            );
        } catch (\Exception $e) {
            return ResponseHelper::returnError('Failed to update store status: ' . $e->getMessage());
        }
    }
}