<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\AssignRoleRequest;
use App\Rules\IndianMobile;
use App\Models\Order;
use App\Models\User;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rule;
use Illuminate\View\View;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role; 
class UserController extends Controller
{
    private function normalizeIndianMobileTo10Digits(?string $mobile): string
    {
        $digits = preg_replace('/\D/', '', (string) $mobile);

        // Strip country code/prefix variations
        if (strlen($digits) === 12 && str_starts_with($digits, '91')) {
            $digits = substr($digits, 2);
        }

        if (strlen($digits) === 11 && str_starts_with($digits, '0')) {
            $digits = substr($digits, 1);
        }

        // If someone pasted a longer number, keep the last 10 digits
        if (strlen($digits) > 10) {
            $digits = substr($digits, -10);
        }

        return $digits;
    }

    public function index(): View
    {
        // Redirect to customers by default for backward compatibility
        return $this->customers();
    }

    public function customers(): View
    {
        $tab = 'customers';
        
        // KPI Values - Only count registered users (customers), not staff
        $todayRegistered = User::whereDate('created_at', today())
            ->where('user_type', 'customer')
            ->count();
            
        $thisWeekRegistered = User::whereBetween('created_at', [now()->startOfWeek(), now()->endOfWeek()])
            ->where('user_type', 'customer')
            ->count();
            
        $thisMonthRegistered = User::whereMonth('created_at', now()->month)
            ->whereYear('created_at', now()->year)
            ->where('user_type', 'customer')
            ->count();
            
        $thisYearRegistered = User::whereYear('created_at', now()->year)
            ->where('user_type', 'customer')
            ->count();
        
        // Get users with permission checks
        $currentUser = Auth::user();
        
        if (!$currentUser) {
            abort(403, 'You must be logged in to view users.');
        }
        
        // Check permissions - use user_type for admin, permissions for staff
        $canViewUsers = $currentUser->isAdmin() || $currentUser->isStaff();
        
        // For staff users, check permissions
        if ($currentUser->isStaff()) {
            try {
                if (method_exists($currentUser, 'can')) {
                    $canViewUsers = $currentUser->can('customers.view');
                }
            } catch (\Exception $e) {
                // Permission system not fully set up
            }
        }
        
        // Check permission to view registered users
        if (!$canViewUsers) {
            abort(403, 'You do not have permission to view registered users.');
        }
        
        // Customer users (customer user_type)
        // Exclude users with staff/admin roles and staff_number to ensure only true customers appear
        $query = User::where('user_type', 'customer')->whereNull('staff_number');
        
        // Apply filters for customers
        if (request()->filled('filter_name')) {
            $query->where('name', 'like', '%' . request('filter_name') . '%');
        }
        
        if (request()->filled('filter_mobile')) {
            $mobileFilter = preg_replace('/^\+91/', '', request('filter_mobile'));
            $mobileFilter = preg_replace('/\D/', '', $mobileFilter);
            if (!empty($mobileFilter)) {
                $query->where('mobile', 'like', '%' . $mobileFilter . '%');
            }
        }
        
        $users = $query->latest()->paginate(20)->withQueryString();

        return view('admin.users.customers', compact(
            'users',
            'todayRegistered',
            'thisWeekRegistered',
            'thisMonthRegistered',
            'thisYearRegistered'
        ));
    }

    public function staff(): View
    {
        // Get users with permission checks
        $currentUser = Auth::user();
        
        if (!$currentUser) {
            abort(403, 'You must be logged in to view users.');
        }
        
        // Check permissions - use user_type for admin, permissions for staff
        $canViewUsers = $currentUser->isAdmin() || $currentUser->isStaff();
        
        // For staff users, check permissions
        if ($currentUser->isStaff()) {
            try {
                if (method_exists($currentUser, 'can')) {
                    $canViewUsers = $currentUser->can('staff.view');
                }
            } catch (\Exception $e) {
                // Permission system not fully set up
            }
        }
        
        // Check permission to view staff
        if (!$canViewUsers) {
            abort(403, 'You do not have permission to view staff members.');
        }
        
        // Show only staff users (exclude admin user_type)
        $locationFilter = request('location_id');
        $search = trim((string) request('q', ''));
        $usersQuery = User::where('user_type', 'staff')
            ->with(['roles', 'stores', 'defaultLocation']);

        if ($locationFilter && (int) $locationFilter > 0) {
            $usersQuery->where(function ($q) use ($locationFilter) {
                $q->where('default_location_id', (int) $locationFilter)
                    ->orWhereHas('stores', fn ($s) => $s->where('locations.id', (int) $locationFilter));
            });
        }

        if ($search !== '') {
            $usersQuery->where(function ($q) use ($search) {
                $q->where('name', 'like', "%{$search}%")
                    ->orWhere('mobile', 'like', "%{$search}%")
                    ->orWhere('email', 'like', "%{$search}%")
                    ->orWhere('staff_number', 'like', "%{$search}%");
            });
        }

        $users = $usersQuery->latest()->paginate(20)->withQueryString();
        $storeLocations = \App\Models\Location::query()->active()->orderBy('type')->orderBy('name')->get();

        // Calculate Sales KPIs for all staff combined
        $todaySales = Order::where('payment_status', 'paid')
            ->whereDate('created_at', today())
            ->whereNotNull('staff_id')
            ->sum('total_payable');

        $thisWeekSales = Order::where('payment_status', 'paid')
            ->whereBetween('created_at', [now()->startOfWeek(), now()->endOfWeek()])
            ->whereNotNull('staff_id')
            ->sum('total_payable');

        $thisMonthSales = Order::where('payment_status', 'paid')
            ->whereMonth('created_at', now()->month)
            ->whereYear('created_at', now()->year)
            ->whereNotNull('staff_id')
            ->sum('total_payable');

        $thisMonthOrders = Order::where('payment_status', 'paid')
            ->whereMonth('created_at', now()->month)
            ->whereYear('created_at', now()->year)
            ->whereNotNull('staff_id')
            ->count();

        $avgOrderValue = $thisMonthOrders > 0 
            ? $thisMonthSales / $thisMonthOrders 
            : 0;

        // Calculate per-staff statistics for current page
        $staffWithStats = $users->getCollection()->map(function ($staff) {
            $orders = Order::where('staff_id', $staff->id)
                ->where('payment_status', 'paid')
                ->get();
            
            return [
                'user' => $staff,
                'total_sales' => $orders->sum('total_payable'),
                'total_orders' => $orders->count(),
                'avg_order_value' => $orders->count() > 0 ? $orders->avg('total_payable') : 0,
                'last_order_date' => $orders->max('created_at'),
            ];
        });

        return view('admin.users.staff', compact(
            'users',
            'staffWithStats',
            'todaySales',
            'thisWeekSales',
            'thisMonthSales',
            'thisMonthOrders',
            'avgOrderValue',
            'storeLocations',
            'locationFilter'
        ));
    }

    public function create(): View
    {
        $currentUser = Auth::user();
        
        // Check permission to create staff
        $this->authorize('staff.create');

        
        // Get all roles ordered by name (matching roles module pattern)
        $roles = Role::orderBy('name')->get();
        $storeLocations = \App\Models\Location::query()->active()->orderBy('type')->orderBy('name')->get();
        $assignedLocationIds = [];
        
        return view('admin.users.create', compact('roles', 'storeLocations', 'assignedLocationIds'));
    }

    public function store(Request $request): RedirectResponse
    {
        $currentUser = Auth::user();
        
        // Check permission to create staff
        $this->authorize('staff.create');
        
        // Normalize mobile before validation (strip formatting / +91 / 0)
        $normalizedMobile = $this->normalizeIndianMobileTo10Digits($request->input('mobile'));
        $request->merge(['mobile' => $normalizedMobile]);
        
        $validated = $request->validate([
            'name' => 'required|string|max:255',
            'mobile' => ['required', 'string', new IndianMobile()],
            'email' => 'nullable|email|max:255|unique:users,email',
            'staff_number' => 'nullable|string|max:50|unique:users,staff_number',
            'password' => 'required|string|min:8|confirmed',
            'roles' => 'required|array',
            'roles.*' => 'exists:roles,name',
            'location_ids' => 'nullable|array',
            'location_ids.*' => 'exists:locations,id',
            'default_location_id' => 'nullable|exists:locations,id',
        ]);
        
        // Check uniqueness with normalized mobile (10 digits)
        if (User::where('mobile', $normalizedMobile)->exists()) {
            return redirect()->back()
                ->withErrors(['mobile' => 'This mobile number is already registered.'])
                ->withInput();
        }
        
        // Use normalized mobile (10 digits only) for storage
        $mobile = $normalizedMobile;
        
        // Determine user_type based on roles
        $userType = 'staff'; // default for staff creation
        if (in_array('admin', $request->roles)) {
            $userType = 'admin';
        }

        $locationIds = array_map('intval', $validated['location_ids'] ?? []);
        $defaultLocationId = $validated['default_location_id'] ?? null;
        if ($defaultLocationId && !in_array((int) $defaultLocationId, $locationIds, true)) {
            $locationIds[] = (int) $defaultLocationId;
        }

        if ($userType === 'staff' && \Illuminate\Support\Facades\Schema::hasTable('locations') && \App\Models\Location::query()->active()->exists() && empty($locationIds)) {
            return redirect()->back()
                ->withErrors(['location_ids' => 'Assign at least one store / location for this staff member.'])
                ->withInput();
        }

        if ($userType === 'staff' && !empty($locationIds) && (!$defaultLocationId || !in_array((int) $defaultLocationId, $locationIds, true))) {
            return redirect()->back()
                ->withErrors(['default_location_id' => 'Choose a default location from the assigned stores.'])
                ->withInput();
        }
        
        $user = User::create([
            'name' => $validated['name'],
            'mobile' => $mobile,
            'email' => $validated['email'],
            'staff_number' => $validated['staff_number'] ?? null,
            'password' => Hash::make($validated['password']),
            'user_type' => $userType,
            'default_location_id' => $defaultLocationId,
            'mobile_verified_at' => now(), // Auto-verify staff mobile
        ]);
        
        // Assign roles
        $user->assignRole($request->roles);

        $sync = [];
        foreach ($locationIds as $locationId) {
            $sync[$locationId] = [
                'is_default' => (int) $locationId === (int) $defaultLocationId,
            ];
        }
        $user->stores()->sync($sync);
        
        // Clear permission cache to ensure roles are refreshed
        app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions();
        
        // Refresh user model to reload roles
        $user->refresh();
        
        // Sync user_type based on assigned roles (in case roles changed user_type)
        $this->syncUserTypeFromRoles($user);
        
        return redirect()->route('admin.users.staff')
            ->with('success', 'Staff member created successfully!');
    }

    public function edit(User $user): View
    {
        $isStaff = $user->user_type === 'staff' || $user->user_type === 'admin';
        
        // Load customer orders and calculate metrics (only for customers)
        $customerMetrics = null;
        $paginatedOrders = null;
        $staffMetrics = null;
        $roles = null;
        
        if ($isStaff) {
            // Calculate staff sales KPIs
            $orders = Order::where('staff_id', $user->id)
                ->where('payment_status', 'paid')
                ->get();
            
            $todaySales = Order::where('staff_id', $user->id)
                ->where('payment_status', 'paid')
                ->whereDate('created_at', today())
                ->sum('total_payable');
            
            $thisWeekSales = Order::where('staff_id', $user->id)
                ->where('payment_status', 'paid')
                ->whereBetween('created_at', [now()->startOfWeek(), now()->endOfWeek()])
                ->sum('total_payable');
            
            $thisMonthSales = Order::where('staff_id', $user->id)
                ->where('payment_status', 'paid')
                ->whereMonth('created_at', now()->month)
                ->whereYear('created_at', now()->year)
                ->sum('total_payable');
            
            $totalSales = $orders->sum('total_payable');
            $totalOrders = $orders->count();
            $avgOrderValue = $totalOrders > 0 ? $totalSales / $totalOrders : 0;
            $lastOrderDate = $orders->max('created_at');
            
            $staffMetrics = [
                'today_sales' => $todaySales,
                'this_week_sales' => $thisWeekSales,
                'this_month_sales' => $thisMonthSales,
                'total_sales' => $totalSales,
                'total_orders' => $totalOrders,
                'avg_order_value' => $avgOrderValue,
                'last_order_date' => $lastOrderDate,
            ];
            
            // Get all roles for role management
            $roles = Role::orderBy('name')->get();
        } else {
            // Load all orders with relationships
            $allOrders = $user->orders()->with(['items', 'coupon'])->latest();
            
            // Calculate metrics
            $totalOrders = $allOrders->count();
            $paidOrders = $user->orders()->where('payment_status', 'paid')->get();
            $totalValue = $paidOrders->sum('total_payable');
            $averageOrder = $paidOrders->count() > 0 ? $totalValue / $paidOrders->count() : 0;
            $lastOrder = $allOrders->first();
            $pendingOrders = $user->orders()->whereNotIn('status', ['delivered', 'cancelled'])->count();
            $completedOrders = $user->orders()->where('status', 'delivered')->count();
            
            // Paginate orders
            $paginatedOrders = $allOrders->paginate(15);
            
            $customerMetrics = [
                'total_orders' => $totalOrders,
                'total_value' => $totalValue,
                'average_order' => $averageOrder,
                'last_order' => $lastOrder,
                'pending_orders' => $pendingOrders,
                'completed_orders' => $completedOrders,
            ];
        }
        
        $storeLocations = collect();
        $assignedLocationIds = [];
        if ($isStaff) {
            $storeLocations = \App\Models\Location::query()->active()->orderBy('type')->orderBy('name')->get();
            $assignedLocationIds = $user->stores()->pluck('locations.id')->all();
        }

        return view('admin.users.edit', compact(
            'user',
            'isStaff',
            'customerMetrics',
            'paginatedOrders',
            'staffMetrics',
            'roles',
            'storeLocations',
            'assignedLocationIds'
        ));
    }

    public function update(Request $request, User $user): RedirectResponse
    {
        $currentUser = Auth::user();

        if (!$currentUser || !$this->canUpdateUser($user, $currentUser)) {
            abort(403, 'You do not have permission to update users.');
        }

        // Only allow updating staff/admin users
        if (!$user->isAdmin() && !$user->isStaff()) {
            return redirect()->back()->with('error', 'This method is only for updating staff/admin users.');
        }

        // Normalize mobile before validation (strip formatting / +91 / 0)
        $normalizedMobile = $this->normalizeIndianMobileTo10Digits($request->input('mobile'));
        $request->merge(['mobile' => $normalizedMobile]);
        
        // Validate the request
        $validated = $request->validate([
            'name' => 'required|string|max:255',
            'mobile' => ['required', 'string', new IndianMobile()],
            'email' => [
                'nullable',
                'email',
                'max:255',
                Rule::unique('users', 'email')->ignore($user->id),
            ],
            'staff_number' => [
                'nullable',
                'string',
                'max:50',
                Rule::unique('users', 'staff_number')->ignore($user->id),
            ],
            'location_ids' => 'nullable|array',
            'location_ids.*' => 'exists:locations,id',
            'default_location_id' => 'nullable|exists:locations,id',
        ], [
            'name.required' => 'Name is required.',
            'mobile.required' => 'Mobile number is required.',
            'email.email' => 'Please enter a valid email address.',
            'email.unique' => 'This email address is already registered.',
            'staff_number.unique' => 'This staff number is already assigned to another staff member.',
        ]);

        // Check uniqueness with normalized mobile (10 digits), ignoring current user
        if (User::where('mobile', $normalizedMobile)->where('id', '!=', $user->id)->exists()) {
            return redirect()->back()
                ->withErrors(['mobile' => 'This mobile number is already registered.'])
                ->withInput();
        }

        // Use normalized mobile (10 digits only) for storage
        $validated['mobile'] = $normalizedMobile;
        $locationIds = array_map('intval', $validated['location_ids'] ?? []);
        $defaultLocationId = $validated['default_location_id'] ?? null;
        unset($validated['location_ids']);

        if ($defaultLocationId && !in_array((int) $defaultLocationId, $locationIds, true)) {
            $locationIds[] = (int) $defaultLocationId;
        }
        $validated['default_location_id'] = $defaultLocationId;

        $isStaffUser = in_array($user->user_type, ['staff', 'admin'], true)
            || $user->hasRole('staff')
            || $user->hasRole('admin');

        if (
            $isStaffUser
            && $user->user_type === 'staff'
            && \Illuminate\Support\Facades\Schema::hasTable('locations')
            && \App\Models\Location::query()->active()->exists()
            && empty($locationIds)
        ) {
            return redirect()->back()
                ->withErrors(['location_ids' => 'Assign at least one store / location for this staff member.'])
                ->withInput();
        }

        if (
            $user->user_type === 'staff'
            && !empty($locationIds)
            && (!$defaultLocationId || !in_array((int) $defaultLocationId, $locationIds, true))
        ) {
            return redirect()->back()
                ->withErrors(['default_location_id' => 'Choose a default location from the assigned stores.'])
                ->withInput();
        }

        try {
            $user->update($validated);

            $sync = [];
            foreach ($locationIds as $locationId) {
                $sync[$locationId] = [
                    'is_default' => (int) $locationId === (int) $defaultLocationId,
                ];
            }
            $user->stores()->sync($sync);

            return redirect()->back()->with('success', 'Staff details updated successfully.');
        } catch (\Exception $e) {
            return redirect()->back()->with('error', 'Failed to update staff details. Please try again.');
        }
    }

    public function assignRole(AssignRoleRequest $request, User $user): RedirectResponse
    {
        $user->syncRoles($request->validated('roles'));

        if ($request->filled('permissions')) {
            $user->syncPermissions($request->validated('permissions'));
        }

        // Clear permission cache to ensure roles are refreshed
        app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions();
        
        // Refresh user model to reload roles
        $user->refresh();

        // Sync user_type based on roles
        $this->syncUserTypeFromRoles($user);

        return redirect()->route('admin.users.index')->with('success', 'Roles and permissions assigned successfully!');
    }

    /**
     * Sync user_type based on user's roles
     */
    private function syncUserTypeFromRoles(User $user): void
    {
        $userType = 'customer'; // default
        
        // Check if user has admin role
        if ($user->isAdmin()) {
            $userType = 'admin';
        } 
        // Check if user has staff role (but not admin)
        elseif ($user->isStaff()) {
            $userType = 'staff';
        }
        
        // Update user_type if it has changed
        if ($user->user_type !== $userType) {
            $user->update(['user_type' => $userType]);
        }
    }

    public function banUser(User $user): RedirectResponse
    {
        $currentUser = Auth::user();

        if (!$currentUser || !$this->canUpdateCustomers($currentUser)) {
            abort(403, 'You do not have permission to ban users.');
        }
        
        // Prevent banning admin users or self
        if (($user->isAdmin() || $user->isStaff()) || $user->id === $currentUser->id) {
            return redirect()->back()->with('error', 'Cannot ban admin/staff users or yourself.');
        }
        
        $user->update(['banned_at' => now()]);
        
        return redirect()->back()->with('success', 'User has been banned successfully.');
    }

    public function unbanUser(User $user): RedirectResponse
    {
        $currentUser = Auth::user();

        if (!$currentUser || !$this->canUpdateCustomers($currentUser)) {
            abort(403, 'You do not have permission to unban users.');
        }
        
        $user->update(['banned_at' => null]);
        
        return redirect()->back()->with('success', 'User has been unbanned successfully.');
    }

    public function changePassword(Request $request, User $user): RedirectResponse
    {
        $currentUser = Auth::user();

        if (!$currentUser || !$this->canUpdateUser($user, $currentUser)) {
            abort(403, 'You do not have permission to change user passwords.');
        }

        // Validate the request
        $request->validate([
            'password' => 'required|string|min:8|confirmed',
            'password_confirmation' => 'required|string|min:8',
        ], [
            'password.required' => 'Password is required.',
            'password.min' => 'Password must be at least 8 characters long.',
            'password.confirmed' => 'Password confirmation does not match.',
            'password_confirmation.required' => 'Password confirmation is required.',
        ]);

        try {
            // Update the user's password
            $user->update([
                'password' => Hash::make($request->password),
            ]);

            return redirect()->back()->with('success', 'Password changed successfully for ' . $user->name . '.');
        } catch (\Exception $e) {
            return redirect()->back()->with('error', 'Failed to change password. Please try again.');
        }
    }

    public function updateCustomerType(Request $request, User $user): RedirectResponse
    {
        $currentUser = Auth::user();

        if (!$currentUser || !$this->canUpdateCustomers($currentUser)) {
            abort(403, 'You do not have permission to update customer type.');
        }

        // Only allow updating customer_type for non-staff users
        if ($user->isAdmin() || $user->isStaff()) {
            return redirect()->back()->with('error', 'Cannot update customer type for staff/admin users.');
        }

        // Validate the request
        $request->validate([
            'customer_type' => 'required|in:retail,wholesale',
        ], [
            'customer_type.required' => 'Customer type is required.',
            'customer_type.in' => 'Customer type must be either retail or wholesale.',
        ]);

        try {
            // Update the user's customer type
            $user->update([
                'customer_type' => $request->customer_type,
            ]);

            $typeLabel = $request->customer_type === 'wholesale' ? 'Wholesale' : 'Retail';
            return redirect()->back()->with('success', "Customer type updated to {$typeLabel} for {$user->name}.");
        } catch (\Exception $e) {
            return redirect()->back()->with('error', 'Failed to update customer type. Please try again.');
        }
    }

    public function updateGstNumber(Request $request, User $user): RedirectResponse
    {
        $currentUser = Auth::user();

        if (!$currentUser || !$this->canUpdateCustomers($currentUser)) {
            abort(403, 'You do not have permission to update GST number.');
        }

        // Only allow updating gst_number for non-staff users (customers)
        if ($user->isAdmin() || $user->isStaff()) {
            return redirect()->back()->with('error', 'Cannot update GST number for staff/admin users.');
        }

        // Validate the request
        $request->validate([
            'gst_number' => [
                'nullable',
                'string',
                'max:15',
                'regex:/^[0-9A-Z]{15}$/',
            ],
        ], [
            'gst_number.max' => 'GST number must be exactly 15 characters.',
            'gst_number.regex' => 'GST number must be 15 alphanumeric characters (e.g., 29XAbbA4369J1PA).',
        ]);

        try {
            // Update the user's GST number
            $user->update([
                'gst_number' => $request->gst_number ? strtoupper($request->gst_number) : null,
            ]);

            $message = $request->gst_number 
                ? "GST number updated to {$request->gst_number} for {$user->name}."
                : "GST number removed for {$user->name}.";
            
            return redirect()->back()->with('success', $message);
        } catch (\Exception $e) {
            return redirect()->back()->with('error', 'Failed to update GST number. Please try again.');
        }
    }

    /**
     * Whether the actor may update customer records (aligned with route middleware).
     */
    private function canUpdateCustomers(User $actor): bool
    {
        if ($actor->isAdmin()) {
            return true;
        }

        if (!method_exists($actor, 'can')) {
            return false;
        }

        return $actor->can('customers.update') || $actor->can('users.update');
    }

    /**
     * Whether the actor may update staff records (aligned with route middleware).
     */
    private function canUpdateStaff(User $actor): bool
    {
        if ($actor->isAdmin()) {
            return true;
        }

        if (!method_exists($actor, 'can')) {
            return false;
        }

        return $actor->can('staff.update') || $actor->can('users.update');
    }

    /**
     * Whether the actor may update the given user (customer vs staff/admin).
     */
    private function canUpdateUser(User $target, User $actor): bool
    {
        if ($target->isAdmin() || $target->isStaff()) {
            return $this->canUpdateStaff($actor);
        }

        return $this->canUpdateCustomers($actor);
    }
}
