<?php

declare(strict_types=1);

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\ExtendServiceExpiryRequest;
use App\Http\Requests\Admin\UpdateServiceRequest;
use App\Models\AuditLog;
use App\Models\Plan;
use App\Models\Service;
use App\Services\Provisioning\ProvisioningFactory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Log;
use Inertia\Inertia;
use Inertia\Response;

class ServiceController extends Controller
{
    public function index(Request $request): Response
    {
        $query = Service::query()
            ->with(['user:id,name,email', 'plan:id,name,service_type,price,billing_cycle']);

        // Include soft-deleted services when requested
        if ($request->boolean('show_archived')) {
            $query->withTrashed();
        }

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

        // Filter by service type
        if ($serviceType = $request->input('service_type')) {
            $query->where('service_type', $serviceType);
        }

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

        $services = $query->latest()->paginate(15)->withQueryString();

        return Inertia::render('Admin/Services/Index', [
            'services' => $services,
            'filters' => [
                'search' => $request->input('search', ''),
                'service_type' => $request->input('service_type', ''),
                'status' => $request->input('status', ''),
                'show_archived' => $request->boolean('show_archived'),
            ],
        ]);
    }

    public function show(int $service): Response
    {
        $service = Service::withTrashed()->findOrFail($service);

        $service->load([
            'user:id,name,email,status',
            'plan:id,name,service_type,price,billing_cycle',
            'subscription:id,ends_at,current_period_end,stripe_status,type',
            'provisioningLogs' => function ($query): void {
                $query->latest()->limit(50);
            },
        ]);

        // Get available plans for plan change (same service type, active)
        $availablePlans = Plan::query()
            ->where('service_type', $service->service_type)
            ->where('status', 'active')
            ->orderBy('price')
            ->get(['id', 'name', 'price', 'billing_cycle']);

        return Inertia::render('Admin/Services/Show', [
            'service' => $service,
            'availablePlans' => $availablePlans,
        ]);
    }

    public function suspend(Service $service): RedirectResponse
    {
        $service->update([
            'status' => 'suspended',
            'suspended_at' => now(),
        ]);

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

        return redirect()->back()->with('success', 'Service has been suspended.');
    }

    public function unsuspend(Service $service): RedirectResponse
    {
        $service->update([
            'status' => 'active',
            'suspended_at' => null,
        ]);

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

        return redirect()->back()->with('success', 'Service has been unsuspended.');
    }

    public function terminate(Service $service): RedirectResponse
    {
        $service->update([
            'status' => 'terminated',
            'terminated_at' => now(),
        ]);

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

        return redirect()->back()->with('success', 'Service has been terminated.');
    }

    public function provision(Service $service, ProvisioningFactory $provisioningFactory): RedirectResponse
    {
        // Check if service is already provisioned
        if ($service->status === 'active' || $service->provisioned_at !== null) {
            return redirect()->back()->with('error', 'Service has already been provisioned.');
        }

        // Check if service has a subscription
        if (! $service->subscription) {
            return redirect()->back()->with('error', 'Service must have an associated subscription to be provisioned.');
        }

        try {
            $provisioningService = $provisioningFactory->make($service->service_type);

            // provision() is idempotent — it reuses the existing Service record
            $provisionedService = $provisioningService->provision($service->subscription);

            AuditLog::create([
                'user_id' => $provisionedService->user_id,
                'admin_id' => auth()->id(),
                'action' => 'manual_provision_service',
                'resource_type' => 'service',
                'resource_id' => $provisionedService->id,
                'ip_address' => request()->ip(),
                'user_agent' => request()->userAgent(),
                'changes' => [
                    'service_type' => $provisionedService->service_type,
                    'platform' => $provisionedService->platform,
                ],
            ]);

            Log::info('Admin manually provisioned service', [
                'admin_id' => auth()->id(),
                'service_id' => $provisionedService->id,
                'service_type' => $provisionedService->service_type,
            ]);

            return redirect()->route('services.show', $provisionedService)->with('success', 'Service has been provisioned successfully.');
        } catch (\Throwable $e) {
            Log::error('Manual service provisioning failed', [
                'admin_id' => auth()->id(),
                'service_id' => $service->id,
                'error' => $e->getMessage(),
            ]);

            return redirect()->back()->with('error', 'Failed to provision service: '.$e->getMessage());
        }
    }

    public function destroy(int $service): RedirectResponse
    {
        $service = Service::withTrashed()->findOrFail($service);

        $service->delete();

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

        return redirect()->route('services.index')->with('success', 'Service has been archived.');
    }

    public function restore(int $service): RedirectResponse
    {
        $service = Service::withTrashed()->findOrFail($service);

        $service->restore();

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

        return redirect()->back()->with('success', 'Service has been restored.');
    }

    public function update(Service $service, UpdateServiceRequest $request): RedirectResponse
    {
        $validated = $request->validated();
        $changes = [];

        // Track plan change
        if (isset($validated['plan_id']) && $validated['plan_id'] !== null && $validated['plan_id'] !== $service->plan_id) {
            $oldPlan = $service->plan;
            $newPlan = Plan::findOrFail($validated['plan_id']);

            $service->update(['plan_id' => $validated['plan_id']]);

            $changes['plan'] = [
                'old' => $oldPlan->name,
                'new' => $newPlan->name,
            ];

            Log::info('Admin changed service plan', [
                'admin_id' => auth()->id(),
                'service_id' => $service->id,
                'old_plan_id' => $oldPlan->id,
                'new_plan_id' => $newPlan->id,
            ]);
        }

        // Track notes update (notes field doesn't exist yet, but keeping for future)
        if (isset($validated['notes'])) {
            $changes['notes'] = [
                'old' => $service->notes ?? null,
                'new' => $validated['notes'],
            ];
        }

        if (empty($changes)) {
            return redirect()->back()->with('info', 'No changes were made to the service.');
        }

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

        return redirect()->back()->with('success', 'Service has been updated successfully.');
    }

    public function extendExpiry(Service $service, ExtendServiceExpiryRequest $request): RedirectResponse
    {
        $validated = $request->validated();
        $newExpiryDate = Carbon::parse($validated['new_expiry_date'])->endOfDay();

        $subscription = $service->subscription;

        if (! $subscription) {
            return redirect()->back()->with('error', 'This service does not have an associated subscription.');
        }

        $oldEndsAt = $subscription->ends_at;

        $subscription->update([
            'ends_at' => $newExpiryDate,
        ]);

        AuditLog::create([
            'user_id' => $service->user_id,
            'admin_id' => auth()->id(),
            'action' => 'extend_service_expiry',
            'resource_type' => 'service',
            'resource_id' => $service->id,
            'ip_address' => request()->ip(),
            'user_agent' => request()->userAgent(),
            'changes' => [
                'old_ends_at' => $oldEndsAt?->toIso8601String(),
                'new_ends_at' => $newExpiryDate->toIso8601String(),
                'reason' => $validated['reason'] ?? null,
            ],
        ]);

        Log::info('Admin extended service expiry', [
            'admin_id' => auth()->id(),
            'service_id' => $service->id,
            'subscription_id' => $subscription->id,
            'old_ends_at' => $oldEndsAt?->toIso8601String(),
            'new_ends_at' => $newExpiryDate->toIso8601String(),
        ]);

        return redirect()->back()->with('success', 'Service expiry date has been extended successfully.');
    }
}
