<?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\Permissions\UserTypeService;
use App\Http\Controllers\Helpers\ResponseHelper;
use App\Http\Controllers\Helpers\UserController;
use App\Models\Market\CooperativeMember;
use App\Models\Market\Organization;
use App\Models\Market\Product;
use App\Models\Market\Store;
use App\Models\User;
use Hypervel\Support\Str;
use Hypervel\Http\Request;
use Hypervel\Support\Facades\Auth;
use Hypervel\Support\Facades\DB;
use Hypervel\Support\Facades\Hash;
use Hypervel\Support\Facades\Response;
use Hypervel\Support\Facades\Validator;

class BatchController
{
    /**
     * Batch create products.
     * Available for Ultimate, Super Operator, and Operator.
     */
    public function batchCreateProducts(Request $request)
    {
        $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]);
        $isStoreOwner = $acctType === UserTypes::STORE_OWNER;

        if (!$isBig3 && !$isStoreOwner) {
            return ResponseHelper::returnUnauthorized();
        }

        $products = $request->input('products', []);
        if (empty($products)) {
            return ResponseHelper::returnError('No products provided');
        }

        $targetStoreHash = $request->input('target_store_hash');
        $targetStore = null;
        if ($targetStoreHash) {
            $targetStore = Store::where('hashkey', $targetStoreHash)->first();
            if (!$targetStore) {
                return ResponseHelper::returnError('Target store not found');
            }
        }

        // STORE_OWNER must operate against a store they actually own/manage.
        // Without that, batch-import has no destination and must be refused
        // up-front so the UI doesn't silently 401 partway through.
        if (!$isBig3) {
            if (!$targetStore) {
                return ResponseHelper::returnError('You must select one of your stores before importing products.', 422);
            }
            $ownsTarget = (int) $targetStore->owner_id === (int) $user->id
                || (int) $targetStore->manager_id === (int) $user->id
                || $targetStore->managers()->where('user_id', $user->id)->exists();
            if (!$ownsTarget) {
                return ResponseHelper::returnError('You can only import products into stores you own.', 403);
            }
        }

        $results = [];
        $errors = [];

        DB::beginTransaction();
        try {
            foreach ($products as $index => $productData) {
                $source = $productData['source'] ?? 'new';

                if ($source === 'existing') {
                    if (!$targetStore) {
                        $errors[] = 'Row ' . ($index + 1) . ': A target store is required to import an existing product.';
                        continue;
                    }

                    $validator = Validator::make($productData, [
                        'product_hash' => 'required|string',
                        'price' => 'nullable|numeric|min:0',
                        'available' => 'nullable|numeric',
                        'description' => 'nullable|string',
                    ]);

                    if ($validator->fails()) {
                        $errors[] = 'Row ' . ($index + 1) . ': ' . implode(', ', $validator->errors()->all());
                        continue;
                    }

                    $existing = Product::where('hashkey', $productData['product_hash'])->first();
                    if (!$existing) {
                        $errors[] = 'Row ' . ($index + 1) . ': Selected global product not found.';
                        continue;
                    }

                    $alreadyAttached = $targetStore->products()->where('prd_items.id', $existing->id)->exists();
                    if ($alreadyAttached) {
                        $errors[] = 'Row ' . ($index + 1) . ": '{$existing->name}' is already in the target store.";
                        continue;
                    }

                    $price = $productData['price'] ?? null;
                    $price = ($price === null || $price === '' || (float) $price <= 0)
                        ? (float) $existing->price
                        : (float) $price;

                    $description = $productData['description'] ?? '';
                    if (trim((string) $description) === '') {
                        $description = (string) ($existing->description ?? '');
                    }

                    $available = (int) ($productData['available'] ?? 0);

                    $targetStore->products()->attach($existing->id, [
                        'available' => $available,
                        'price' => $price,
                        'description' => $description,
                        'is_active' => true,
                    ]);

                    $results[] = $existing->hashkey;
                    continue;
                }

                $validator = Validator::make($productData, [
                    'name' => 'required|string|max:255',
                    'price' => 'required|numeric|min:0',
                    'available' => 'required|numeric',
                    'unitname' => 'required|string|max:100',
                    'description' => 'nullable|string',
                    'category' => 'nullable|string|max:255',
                    'subcategory' => 'nullable|string|max:255',
                    'barcode' => 'nullable|string|max:255',
                    'photourl' => 'nullable|array',
                    'photourl.*' => 'nullable|string',
                ]);

                if ($validator->fails()) {
                    $errors[] = "Row " . ($index + 1) . ": " . implode(', ', $validator->errors()->all());
                    continue;
                }

                // Reject if a global product with this name already exists.
                // Owners should pick the existing one via the fuzzy-search
                // modal instead of creating a duplicate global entry.
                $duplicate = Product::whereRaw('LOWER(TRIM(name)) = ?', [strtolower(trim((string) $productData['name']))])
                    ->first();
                if ($duplicate) {
                    $errors[] = "Row " . ($index + 1) . ": '{$productData['name']}' already exists globally. Use 'Pick existing' to import it instead.";
                    continue;
                }

                $product = Product::create([
                    'name' => $productData['name'],
                    'description' => $productData['description'] ?? '',
                    'price' => $productData['price'],
                    'unitname' => $productData['unitname'],
                    'available' => $productData['available'],
                    'barcode' => $productData['barcode'] ?? null,
                    'category' => $productData['category'] ?? null,
                    'subcategory' => $productData['subcategory'] ?? null,
                    'photourl' => $productData['photourl'] ?? [],
                    'created_by' => $user->id,
                    'is_active' => true,
                ]);

                if ($targetStore) {
                    $targetStore->products()->attach($product->id, [
                        'available' => (int) $productData['available'],
                        'price' => (float) $productData['price'],
                        'description' => $productData['description'] ?? '',
                        'is_active' => true,
                    ]);
                }

                $results[] = $product->hashkey;
            }

            if (!empty($errors)) {
                DB::rollBack();
                return Response::json(['success' => false, 'message' => 'Batch creation failed', 'errors' => $errors], 422);
            }

            DB::commit();
            return Response::json(['success' => true, 'count' => count($results), 'data' => $results]);
        } catch (\Throwable $th) {
            DB::rollBack();
            return ResponseHelper::returnError($th->getMessage());
        }
    }

    /**
     * Serve the batch products Excel template as a file download.
     * Available for all authenticated users with batch module access.
     */
    public function downloadProductTemplate()
    {
        $templatePath = BASE_PATH . '/resources/templates/batch-products-template.xlsx';

        if (!file_exists($templatePath)) {
            return Response::json(['success' => false, 'message' => 'Template file not found.'], 404);
        }

        $fileContent = file_get_contents($templatePath);
        $filename = 'bukidbounty-batch-products-template.xlsx';

        return Response::make($fileContent, 200, [
            'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
            'Content-Disposition' => 'attachment; filename="' . $filename . '"',
            'Content-Length' => strlen($fileContent),
            'Cache-Control' => 'no-cache, no-store, must-revalidate',
            'Pragma' => 'no-cache',
            'Expires' => '0',
        ]);
    }

    /**
     * Batch create stores.
     * Available for Ultimate, Super Operator, and Operator.
     */
    public function batchCreateStores(Request $request)
    {
        $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) {
            return ResponseHelper::returnUnauthorized();
        }

        $stores = $request->input('stores', []);
        if (empty($stores)) {
            return ResponseHelper::returnError('No stores provided');
        }

        $results = [];
        $errors = [];

        DB::beginTransaction();
        try {
            foreach ($stores as $index => $storeData) {
                $validator = Validator::make($storeData, [
                    'name' => 'required|string|max:255',
                    'description' => 'required|string',
                    'address' => 'required|string',
                    'category' => 'nullable|string|max:100',
                    'subcategory' => 'nullable|string|max:100',
                    'owner_hash' => 'nullable|string',
                ]);

                if ($validator->fails()) {
                    $errors[] = "Row " . ($index + 1) . ": " . implode(', ', $validator->errors()->all());
                    continue;
                }

                $ownerId = null;
                if (!empty($storeData['owner_hash'])) {
                    $ownerId = UserController::findUserIdByHash($storeData['owner_hash']);
                }

                $storeCode = StoreController::generateStoreCode($storeData['category'] ?? 'General');

                $store = Store::create([
                    'storecode' => $storeCode,
                    'name' => $storeData['name'],
                    'description' => $storeData['description'],
                    'address' => $storeData['address'],
                    'category' => $storeData['category'] ?? 'General',
                    'subcategory' => $storeData['subcategory'] ?? '',
                    'owner_id' => $ownerId,
                    'created_by' => $user->id,
                    'is_active' => true,
                    'status' => 'active',
                ]);

                $results[] = $store->hashkey;
            }

            if (!empty($errors)) {
                DB::rollBack();
                return Response::json(['success' => false, 'message' => 'Batch creation failed', 'errors' => $errors], 422);
            }

            DB::commit();
            return Response::json(['success' => true, 'count' => count($results), 'data' => $results]);
        } catch (\Throwable $th) {
            DB::rollBack();
            return ResponseHelper::returnError($th->getMessage());
        }
    }

    /**
     * Batch create users.
     * Available for Ultimate, Super Operator, and Operator.
     */
    public function batchCreateUsers(Request $request)
    {
        $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) {
            return ResponseHelper::returnUnauthorized();
        }

        $usersData = $request->input('users', []);
        if (empty($usersData)) {
            return ResponseHelper::returnError('No users provided');
        }

        $results = [];
        $errors = [];

        DB::beginTransaction();
        try {
            foreach ($usersData as $index => $data) {
                $validator = Validator::make($data, [
                    'username' => 'required|string|max:255|unique:users,username',
                    'name' => 'required|string|max:255',
                    'mobile_number' => ['required', 'string', 'max:20', 'unique:users,mobile_number', 'regex:/^(09|\+639)\d{9}$/'],
                    'password' => 'required|string|min:6',
                    'type' => 'required|string',
                    'parent_hash' => 'nullable|string',
                ]);

                if ($validator->fails()) {
                    $errors[] = "Row " . ($index + 1) . ": " . implode(', ', $validator->errors()->all());
                    continue;
                }

                $usertypeEnum = UserTypes::tryFrom($data['type']);
                if (!$usertypeEnum) {
                    $errors[] = "Row " . ($index + 1) . ": Invalid User Type";
                    continue;
                }

                // Check if creator is allowed to create this user type
                if ($acctType !== UserTypes::ULTIMATE) {
                    $allowedTypes = UserTypeService::getAllowedUserTypes($acctType);
                    if (!in_array($usertypeEnum, $allowedTypes)) {
                        $errors[] = "Row " . ($index + 1) . ": You are not allowed to create user type '{$data['type']}'";
                        continue;
                    }
                }

                $parentId = $user->id; // Default to creator
                if (!empty($data['parent_hash'])) {
                    $parent = User::where('hashkey', $data['parent_hash'])->first();
                    if ($parent) {
                        $parentId = $parent->id;
                    }
                }

                $newUser = User::create([
                    'username' => $data['username'],
                    'name' => $data['name'],
                    'mobile_number' => $data['mobile_number'],
                    'password' => Hash::make($data['password']),
                    'acct_type' => $data['type'],
                    'parentuid' => $parentId,
                    'active' => true,
                ]);

                $results[] = $newUser->hashkey;
            }

            if (!empty($errors)) {
                DB::rollBack();
                return Response::json(['success' => false, 'message' => 'Batch creation failed', 'errors' => $errors], 422);
            }

            DB::commit();
            return Response::json(['success' => true, 'count' => count($results), 'data' => $results]);
        } catch (\Throwable $th) {
            DB::rollBack();
            return ResponseHelper::returnError($th->getMessage());
        }
    }

    /**
     * Batch create cooperatives.
     * Available for Ultimate and Super Operator.
     */
    public function batchCreateCooperatives(Request $request)
    {
        $user = Auth::user();
        if (!$user) return ResponseHelper::returnUnauthorized();

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

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

        $cooperatives = $request->input('cooperatives', []);
        if (empty($cooperatives)) {
            return ResponseHelper::returnError('No cooperatives provided');
        }

        $results = [];
        $errors = [];

        DB::beginTransaction();
        try {
            foreach ($cooperatives as $index => $data) {
                $validator = Validator::make($data, [
                    'name' => 'required|string|max:255',
                    'address' => 'nullable|string',
                    'registration_number' => 'nullable|string|max:255',
                    'cin' => 'nullable|string|max:255',
                    'tin' => 'nullable|string|max:255',
                    'cooperative_type' => 'nullable|string|max:100',
                    'cooperative_category' => 'nullable|string|max:100',
                    'registration_date' => 'nullable|date',
                    'contact_person' => 'nullable|string|max:255',
                    'contact_number' => 'nullable|string|max:50',
                    'contact_email' => 'nullable|email|max:255',
                ]);

                if ($validator->fails()) {
                    $errors[] = "Row " . ($index + 1) . ": " . implode(', ', $validator->errors()->all());
                    continue;
                }

                $cooperative = new Organization([
                    'hashkey' => Str::random(64),
                    'name' => trim($data['name']),
                    'type' => 'COOPERATIVE',
                    'address' => trim($data['address'] ?? ''),
                    'registration_number' => trim($data['registration_number'] ?? ''),
                    'cin' => trim($data['cin'] ?? ''),
                    'tin' => trim($data['tin'] ?? ''),
                    'cooperative_type' => trim($data['cooperative_type'] ?? ''),
                    'cooperative_category' => trim($data['cooperative_category'] ?? ''),
                    'registration_date' => $data['registration_date'] ?? null,
                    'contact_person' => trim($data['contact_person'] ?? ''),
                    'contact_number' => trim($data['contact_number'] ?? ''),
                    'contact_email' => trim($data['contact_email'] ?? ''),
                    'is_active' => true,
                    'created_by' => $user->id,
                ]);

                if (!$cooperative->save()) {
                    $errors[] = "Row " . ($index + 1) . ": Failed to save cooperative";
                    continue;
                }

                $results[] = $cooperative->hashkey;
            }

            if (!empty($errors)) {
                DB::rollBack();
                return Response::json(['success' => false, 'message' => 'Batch creation failed', 'errors' => $errors], 422);
            }

            DB::commit();
            return Response::json(['success' => true, 'count' => count($results), 'data' => $results]);
        } catch (\Throwable $th) {
            DB::rollBack();
            return ResponseHelper::returnError($th->getMessage());
        }
    }

    /**
     * Batch add cooperative members with automatic user account creation.
     * Each row creates a new User and a corresponding CooperativeMember in one transaction.
     */
    public function batchCreateCooperativeMembers(Request $request)
    {
        $user = Auth::user();
        if (!$user) return ResponseHelper::returnUnauthorized();

        if (!UserPermissions::isActionPermitted($user->acct_type, UserActions::ManageOrganizations)) {
            return ResponseHelper::returnUnauthorized();
        }

        $cooperativeHash = $request->input('cooperative_hash');
        $members = $request->input('members', []);

        if (!$cooperativeHash) {
            return ResponseHelper::returnError('Cooperative hash is required');
        }
        if (empty($members)) {
            return ResponseHelper::returnError('No members provided');
        }

        $cooperative = Organization::where('hashkey', $cooperativeHash)
            ->where('type', 'COOPERATIVE')
            ->first();

        if (!$cooperative) {
            return ResponseHelper::returnError('Cooperative not found', 404);
        }

        $parentUser = User::where('id', $cooperative->created_by)->first() ?? $user;

        $results = [];
        $errors = [];

        DB::beginTransaction();
        try {
            foreach ($members as $index => $data) {
                $validator = Validator::make($data, [
                    'username' => 'required|string|max:255|unique:users,username',
                    'name' => 'required|string|max:255',
                    'mobile_number' => ['required', 'string', 'max:20', 'unique:users,mobile_number', 'regex:/^(09|\+639)\d{9}$/'],
                    'password' => 'required|string|min:6',
                    'role' => 'nullable|string|max:50',
                    'membership_type' => 'nullable|string|max:50',
                ]);

                if ($validator->fails()) {
                    $errors[] = 'Row ' . ($index + 1) . ': ' . implode(', ', $validator->errors()->all());
                    continue;
                }

                $newUser = new User();
                $newUser->username = $data['username'];
                $newUser->name = $data['name'];
                $newUser->mobile_number = $data['mobile_number'];
                $newUser->password = Hash::make($data['password']);
                $newUser->parentuid = $parentUser->id;
                $newUser->acct_type = 'user';
                $newUser->active = true;
                $newUser->save();

                $member = new CooperativeMember([
                    'hashkey' => Str::random(64),
                    'organization_id' => $cooperative->id,
                    'user_id' => $newUser->id,
                    'role' => $data['role'] ?? 'MEMBER',
                    'membership_type' => $data['membership_type'] ?? null,
                    'joined_at' => now(),
                    'is_active' => true,
                    'created_by' => $user->id,
                ]);

                if (!$member->save()) {
                    $errors[] = 'Row ' . ($index + 1) . ': Failed to save membership';
                    continue;
                }

                $results[] = [
                    'user_hashkey' => $newUser->hashkey,
                    'member_hashkey' => $member->hashkey,
                ];
            }

            if (!empty($errors)) {
                DB::rollBack();
                return Response::json(['success' => false, 'message' => 'Batch creation failed', 'errors' => $errors], 422);
            }

            DB::commit();
            return Response::json(['success' => true, 'count' => count($results), 'data' => $results]);
        } catch (\Throwable $th) {
            DB::rollBack();
            return ResponseHelper::returnError($th->getMessage());
        }
    }
}
