<?php

namespace App\Services\Module;

use App\Contracts\InstalledModuleRepositoryContract;
use App\Contracts\ProductOwnerClientContract;
use App\DTOs\ProductOwner\ModulePurchaseResponseData;
use App\Enums\ModuleStatus;
use App\Services\Audit\AuditLogService;
use App\Services\Notifications\EventNotificationService;
use App\Services\Permission\PermissionRegistry;
use App\Services\ProductOwner\InstallationSyncService;
use App\Services\Wallet\WalletLedgerService;
use App\Support\ProductOwnerUnavailable;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
use RuntimeException;

class ModuleInstallService
{
    public function __construct(
        private readonly ProductOwnerClientContract $productOwnerClient,
        private readonly InstalledModuleRepositoryContract $installedModules,
        private readonly ModuleManager $moduleManager,
        private readonly AuditLogService $auditLog,
        private readonly WalletLedgerService $walletLedger,
        private readonly InstallationSyncService $installationSync,
        private readonly EventNotificationService $notifications,
        private readonly PermissionRegistry $permissionRegistry,
    ) {}

    public function install(string $moduleUuid): void
    {
        $installationId = config('product_owner.installation_id');

        if (empty($installationId)) {
            throw new RuntimeException('Installation is not configured.');
        }

        $productOwnerOffline = false;

        if (! $this->installationSync->ensureRegistered()) {
            if ($this->installationSync->isPlatformUnavailable()) {
                $productOwnerOffline = true;
            } else {
                throw new RuntimeException('Installation is not registered with the Product Owner platform.');
            }
        }

        if ($this->installedModules->findByModuleUuid($moduleUuid)) {
            throw new RuntimeException('This module is already installed.');
        }

        $definition = $this->moduleManager->findByUuid($moduleUuid);

        if (! $definition) {
            throw new RuntimeException('This module is not available for installation on this application.');
        }

        $purchase = $productOwnerOffline
            ? $this->offlinePurchaseResponse()
            : $this->productOwnerClient->purchaseModule($installationId, $moduleUuid);

        if (! $purchase->success) {
            if (ProductOwnerUnavailable::isUnavailableMessage($purchase->message)) {
                $purchase = $this->offlinePurchaseResponse();
            } else {
                throw new RuntimeException($purchase->message ?? 'Module purchase failed.');
            }
        }

        if (($purchase->amountCharged ?? 0) > 0 || $purchase->referenceNumber) {
            $this->walletLedger->recordPurchase($purchase, $moduleUuid, $definition->name());
        }

        $this->installedModules->create([
            'module_uuid' => $moduleUuid,
            'name' => $definition->name(),
            'version' => $definition->version(),
            'status' => ModuleStatus::Installed,
            'installed_at' => now(),
        ]);

        $this->initializeRemoteModule($definition, $productOwnerOffline);
        $this->moduleManager->boot($definition);
        $this->permissionRegistry->grantModulePermissionsToDefaultRoles($definition->permissions());

        $this->auditLog->log('module.installed', null, [
            'module_uuid' => $moduleUuid,
            'module_name' => $definition->name(),
            'amount_charged' => $purchase->amountCharged,
            'wallet_reference' => $purchase->referenceNumber,
            'product_owner_offline' => $productOwnerOffline,
        ]);

        $user = Auth::user();

        if ($user instanceof \App\Models\User) {
            $this->notifications->send('module_purchased', $user, [
                'module_name' => $definition->name(),
                'amount' => number_format((float) ($purchase->amountCharged ?? 0), 2),
                'currency' => $purchase->currency ?? config('platform.currency', 'INR'),
            ]);
        }
    }

    private function offlinePurchaseResponse(): ModulePurchaseResponseData
    {
        return new ModulePurchaseResponseData(
            success: true,
            message: 'Module installed locally while Product Owner platform is unavailable.',
            amountCharged: 0,
        );
    }

    private function initializeRemoteModule(ModuleDefinition $definition, bool $productOwnerOffline = false): void
    {
        if (! $definition->isShell()) {
            $this->runModuleSeeder($definition->slug);

            return;
        }

        $installationId = config('product_owner.installation_id');
        $uuid = $definition->uuid();

        if (empty($installationId) || empty($uuid) || $productOwnerOffline) {
            return;
        }

        if (! $this->productOwnerClient->initializeModule($installationId, $uuid)) {
            throw new RuntimeException('Module runtime initialization failed.');
        }
    }

    private function runModuleSeeder(string $slug): void
    {
        $seederClass = 'Modules\\'.Str::studly($slug).'\\Seeders\\'.Str::studly($slug).'Seeder';

        if (! class_exists($seederClass)) {
            return;
        }

        app($seederClass)->run();
    }
}
