<?php

declare(strict_types=1);

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\UpdateCustomerRequest;
use App\Models\AuditLog;
use App\Models\Invoice;
use App\Models\LoginHistory;
use App\Models\Order;
use App\Models\Plan;
use App\Models\Service;
use App\Models\User;
use App\Notifications\AdminNotification;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Str;
use Inertia\Inertia;
use Inertia\Response;

class CustomerController extends Controller
{
    public function index(Request $request): Response
    {
        $query = User::role('customer')
            ->withCount(['services', 'billingInvoices as invoices_count'])
            ->with('subscriptions');

        // Search by name or email
        if ($search = $request->input('search')) {
            $query->where(function ($q) use ($search): void {
                $q->where('name', 'like', "%{$search}%")
                    ->orWhere('email', 'like', "%{$search}%");
            });
        }

        // Filter by status
        if ($status = $request->input('status')) {
            $query->where('status', $status);
        }

        // Sort
        $sortBy = $request->input('sort', 'created_at');
        $sortDir = $request->input('direction', 'desc');
        $allowedSorts = ['name', 'email', 'created_at', 'status'];

        if (in_array($sortBy, $allowedSorts, true)) {
            $query->orderBy($sortBy, $sortDir === 'asc' ? 'asc' : 'desc');
        }

        $customers = $query->paginate(15)->withQueryString();

        // Add subscriptions_count manually since it's a Cashier relationship
        $customers->getCollection()->transform(function (User $user): User {
            $user->setAttribute('subscriptions_count', $user->subscriptions->count());
            unset($user->subscriptions);

            return $user;
        });

        return Inertia::render('Admin/Customers/Index', [
            'customers' => $customers,
            'filters' => [
                'search' => $request->input('search', ''),
                'status' => $request->input('status', ''),
                'sort' => $sortBy,
                'direction' => $sortDir,
            ],
        ]);
    }

    public function show(User $user): Response
    {
        $user->load(['profile', 'services.plan']);

        // Load subscriptions with plan info via join
        $subscriptions = $user->subscriptions()
            ->select([
                'subscriptions.id',
                'subscriptions.user_id',
                'subscriptions.plan_id',
                'subscriptions.type',
                'subscriptions.stripe_status',
                'subscriptions.gateway',
                'subscriptions.current_period_start',
                'subscriptions.current_period_end',
                'subscriptions.ends_at',
                'subscriptions.created_at',
            ])
            ->leftJoin('plans', 'subscriptions.plan_id', '=', 'plans.id')
            ->addSelect([
                'plans.name as plan_name',
                'plans.price as plan_price',
                'plans.billing_cycle as plan_billing_cycle',
            ])
            ->orderByDesc('subscriptions.created_at')
            ->get();

        $recentInvoices = $user->billingInvoices()
            ->latest()
            ->limit(10)
            ->get(['id', 'user_id', 'number', 'total', 'status', 'gateway', 'created_at']);

        $auditLogs = AuditLog::query()
            ->where(function ($q) use ($user): void {
                $q->where('user_id', $user->id)
                    ->orWhere(function ($q2) use ($user): void {
                        $q2->where('resource_type', 'user')
                            ->where('resource_id', $user->id);
                    });
            })
            ->with(['user:id,name,email', 'admin:id,name,email'])
            ->latest()
            ->paginate(15, ['*'], 'audit_page');

        $plans = Plan::query()
            ->where('status', 'active')
            ->orderBy('service_type')
            ->orderBy('price')
            ->get(['id', 'name', 'price', 'billing_cycle', 'service_type']);

        $loginHistories = LoginHistory::query()
            ->forUser($user->id)
            ->latest()
            ->limit(20)
            ->get();

        return Inertia::render('Admin/Customers/Show', [
            'customer' => $user,
            'subscriptions' => $subscriptions,
            'recentInvoices' => $recentInvoices,
            'auditLogs' => $auditLogs,
            'plans' => $plans,
            'loginHistories' => $loginHistories,
        ]);
    }

    public function edit(User $user): Response
    {
        return Inertia::render('Admin/Customers/Edit', [
            'customer' => $user,
        ]);
    }

    public function update(UpdateCustomerRequest $request, User $user): RedirectResponse
    {
        $oldStatus = $user->status;

        $user->update($request->validated());

        // Log status change if it occurred
        if ($oldStatus !== $user->status) {
            AuditLog::create([
                'user_id' => $user->id,
                'admin_id' => $request->user()->id,
                'action' => 'customer_status_changed',
                'resource_type' => 'user',
                'resource_id' => $user->id,
                'ip_address' => $request->ip(),
                'user_agent' => $request->userAgent(),
                'changes' => ['old_status' => $oldStatus, 'new_status' => $user->status],
            ]);
        }

        AuditLog::create([
            'user_id' => $user->id,
            'admin_id' => $request->user()->id,
            'action' => 'customer_updated',
            'resource_type' => 'user',
            'resource_id' => $user->id,
            'ip_address' => $request->ip(),
            'user_agent' => $request->userAgent(),
            'changes' => $request->validated(),
        ]);

        return redirect()->route('customers.show', $user)
            ->with('success', 'Customer updated successfully.');
    }

    public function suspend(User $user): RedirectResponse
    {
        $user->update(['status' => 'suspended']);

        AuditLog::create([
            'user_id' => $user->id,
            'admin_id' => auth()->id(),
            'action' => 'suspend_account',
            'resource_type' => 'user',
            'resource_id' => $user->id,
            'ip_address' => request()->ip(),
            'user_agent' => request()->userAgent(),
        ]);

        return redirect()->back()->with('success', "Customer {$user->name} has been suspended.");
    }

    public function unsuspend(User $user): RedirectResponse
    {
        $user->update(['status' => 'active']);

        AuditLog::create([
            'user_id' => $user->id,
            'admin_id' => auth()->id(),
            'action' => 'unsuspend_account',
            'resource_type' => 'user',
            'resource_id' => $user->id,
            'ip_address' => request()->ip(),
            'user_agent' => request()->userAgent(),
        ]);

        return redirect()->back()->with('success', "Customer {$user->name} has been unsuspended.");
    }

    public function purge(Request $request, User $user): RedirectResponse
    {
        if ($user->isAdmin()) {
            return redirect()->back()->with('error', 'Cannot purge admin users.');
        }

        $userName = $user->name;
        $userEmail = $user->email;

        DB::transaction(function () use ($user, $request): void {
            // Audit log BEFORE destructive operations
            AuditLog::create([
                'admin_id' => $request->user()->id,
                'action' => 'customer_purged',
                'resource_type' => 'user',
                'resource_id' => $user->id,
                'ip_address' => $request->ip(),
                'user_agent' => $request->userAgent(),
                'changes' => [
                    'email' => $user->email,
                    'name' => $user->name,
                ],
            ]);

            // Terminate services on provisioning platforms before purging
            foreach ($user->services as $service) {
                if ($service->platform_service_id) {
                    try {
                        $provisioner = \App\Services\Provisioning\ProvisioningFactory::make($service->service_type);
                        $provisioner->terminate($service);
                    } catch (\Exception $e) {
                        \Illuminate\Support\Facades\Log::error("Failed to terminate service {$service->id} during customer purge: {$e->getMessage()}");
                    }
                }
            }

            // Force-delete all related data (services use SoftDeletes, but user is being permanently purged)
            $user->services()->forceDelete();
            $user->billingInvoices()->delete();
            $user->orders()->delete();
            $user->subscriptions()->delete();
            AuditLog::query()->where('user_id', $user->id)->delete();

            // Delete the user
            $user->delete();
        });

        return redirect()->route('customers.index')
            ->with('success', "Customer {$userName} ({$userEmail}) has been permanently deleted.");
    }

    public function resetPassword(Request $request, User $user): RedirectResponse
    {
        $newPassword = Str::random(16);

        $user->update([
            'password' => Hash::make($newPassword),
        ]);

        AuditLog::create([
            'user_id' => $user->id,
            'admin_id' => $request->user()->id,
            'action' => 'password_reset',
            'resource_type' => 'user',
            'resource_id' => $user->id,
            'ip_address' => $request->ip(),
            'user_agent' => $request->userAgent(),
        ]);

        // Send email with new password
        $user->notify(new \App\Notifications\AdminPasswordResetNotification($newPassword));

        return redirect()->back()
            ->with('success', "Password reset email sent to {$user->email}.");
    }

    public function sendNotification(Request $request, User $user): RedirectResponse
    {
        $request->validate([
            'subject' => 'required|string|max:255',
            'message' => 'required|string',
        ]);

        AuditLog::create([
            'user_id' => $user->id,
            'admin_id' => $request->user()->id,
            'action' => 'notification_sent',
            'resource_type' => 'user',
            'resource_id' => $user->id,
            'ip_address' => $request->ip(),
            'user_agent' => $request->userAgent(),
            'changes' => [
                'subject' => $request->input('subject'),
            ],
        ]);

        $user->notify(new AdminNotification(
            $request->input('subject'),
            $request->input('message')
        ));

        return redirect()->back()
            ->with('success', "Notification sent to {$user->email}.");
    }

    public function placeOrder(Request $request, User $user): RedirectResponse
    {
        $request->validate([
            'plan_id' => 'required|exists:plans,id',
            'billing_cycle' => 'required|in:monthly,quarterly,semi_annual,annual',
        ]);

        $plan = Plan::query()->findOrFail($request->input('plan_id'));

        DB::transaction(function () use ($user, $plan, $request): void {
            // Map service type to provisioning platform
            $platformMap = [
                'vps' => 'virtfusion',
                'dedicated' => 'synergycp',
                'hosting' => 'enhance',
                'game' => 'pterodactyl',
            ];

            // Create service
            $service = Service::query()->create([
                'user_id' => $user->id,
                'plan_id' => $plan->id,
                'service_type' => $plan->service_type,
                'platform' => $platformMap[$plan->service_type] ?? $plan->service_type,
                'status' => 'active',
                'credentials' => [],
            ]);

            // Create order (linked to service)
            $order = Order::query()->create([
                'user_id' => $user->id,
                'plan_id' => $plan->id,
                'service_id' => $service->id,
                'order_number' => 'ORD-'.strtoupper(Str::random(10)),
                'total' => $plan->price,
                'status' => 'completed',
                'payment_gateway' => 'manual',
                'admin_notes' => 'Order placed by admin',
                'completed_at' => now(),
            ]);

            // Create invoice
            Invoice::query()->create([
                'user_id' => $user->id,
                'number' => 'INV-'.strtoupper(Str::random(10)),
                'tax' => 0,
                'total' => $plan->price,
                'status' => 'paid',
                'gateway' => 'manual',
                'paid_at' => now(),
            ]);

            AuditLog::create([
                'user_id' => $user->id,
                'admin_id' => $request->user()->id,
                'action' => 'order_placed',
                'resource_type' => 'order',
                'resource_id' => $order->id,
                'ip_address' => $request->ip(),
                'user_agent' => $request->userAgent(),
                'changes' => [
                    'plan_id' => $plan->id,
                    'plan_name' => $plan->name,
                ],
            ]);
        });

        return redirect()->back()
            ->with('success', "Order for {$plan->name} created successfully.");
    }

    public function forceLogout(Request $request, User $user): RedirectResponse
    {
        // Cycle remember token to invalidate "remember me" cookies
        $user->forceFill([
            'remember_token' => Str::random(60),
        ])->save();

        // Clear trusted devices so they cannot bypass 2FA
        $user->trustedDevices()->delete();

        // Invalidate all Redis sessions for this user.
        // Redis session keys are prefixed with the app name, and the session
        // payload contains the user ID. We iterate matching keys and delete
        // those belonging to the target user.
        try {
            $prefix = config('cache.prefix', 'laravel').'_cache:';
            $connection = config('session.connection') ?? 'default';

            // Use Redis SCAN to find session keys without blocking
            $cursor = '0';
            $sessionPrefix = config('session.cookie', 'laravel_session');

            do {
                $result = Redis::connection($connection)->scan($cursor, ['match' => '*', 'count' => 100]);

                if ($result === false) {
                    break;
                }

                [$cursor, $keys] = $result;

                foreach ($keys as $key) {
                    try {
                        $value = Redis::connection($connection)->get($key);

                        if ($value && is_string($value)) {
                            $decoded = @unserialize($decoded = $value);

                            if (is_array($decoded) && isset($decoded['_token'])) {
                                // Check if this session belongs to the target user
                                $loginWeb = $decoded['login_web_'.sha1('Illuminate\Auth\SessionGuard')] ?? null;

                                if ($loginWeb && (int) $loginWeb === $user->id) {
                                    Redis::connection($connection)->del($key);
                                }
                            }
                        }
                    } catch (\Exception) {
                        // Skip keys that cannot be read
                    }
                }
            } while ($cursor !== '0');
        } catch (\Exception $e) {
            \Illuminate\Support\Facades\Log::warning("Could not clear Redis sessions during force logout: {$e->getMessage()}");
        }

        AuditLog::create([
            'user_id' => $user->id,
            'admin_id' => $request->user()->id,
            'action' => 'force_logout',
            'resource_type' => 'user',
            'resource_id' => $user->id,
            'ip_address' => $request->ip(),
            'user_agent' => $request->userAgent(),
        ]);

        return redirect()->back()
            ->with('success', "All sessions for {$user->name} have been invalidated.");
    }
}
