<?php

namespace App\Services\Voucher;

use App\Enums\UserAccessLevel;
use App\Models\HotspotVoucher;
use App\Models\NasMikrotik;
use App\Models\NasPackageProfile;
use App\Models\PaymentGatewayAccount;
use App\Models\TenantPaymentSetting;
use App\Models\User;
use App\Models\VoucherAgent;
use App\Models\VoucherLoginPageSetting;
use App\Models\VoucherSale;
use App\Services\Wallet\WalletService;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;

class VoucherService
{
    public function __construct(protected WalletService $wallets) {}

    public function sales(array $filters, User $actor, ?int $tenantId)
    {
        return VoucherSale::with(['agent.user:id,name,username', 'packageProfile:id,name,external_profile_name,session_timeout', 'creator:id,name'])
            ->where('tenant_id', $tenantId)
            ->when($actor->access_level === UserAccessLevel::CUSTOMER, fn ($q) => $q->whereHas('agent', fn ($x) => $x->where('user_id', $actor->id)))
            ->when($filters['search'] ?? null, fn ($q, $search) => $q->where('sale_number', 'ilike', "%{$search}%"))
            ->when($filters['payment_status'] ?? null, fn ($q, $value) => $q->where('payment_status', $value))
            ->latest('id')->paginate($filters['per_page'] ?? 10);
    }

    public function saleDetail(VoucherSale $sale, User $actor, ?int $tenantId): VoucherSale
    {
        $this->authorizeTenant($sale->tenant_id, $tenantId, $actor);
        if ($actor->access_level === UserAccessLevel::CUSTOMER && $sale->agent?->user_id !== $actor->id) {
            abort(403);
        }

        $sale->load(['agent.user:id,name,username', 'packageProfile', 'vouchers.mikrotik:id,name']);
        $sale->vouchers->each(fn (HotspotVoucher $voucher) => $voucher->setAttribute('access_password', $voucher->password));

        return $sale;
    }

    public function createSale(array $data, User $actor, ?int $tenantId): VoucherSale
    {
        if (! $tenantId) {
            throw ValidationException::withMessages(['tenant_id' => 'Tenant aktif wajib tersedia.']);
        }
        $package = NasPackageProfile::whereKey($data['nas_package_profile_id'])->where('tenant_id', $tenantId)
            ->where('service_type', 'hotspot')->where('status', 'active')->firstOrFail();
        if (! empty($data['nas_mikrotik_id']) && ! NasMikrotik::whereKey($data['nas_mikrotik_id'])->where('tenant_id', $tenantId)->exists()) {
            throw ValidationException::withMessages(['nas_mikrotik_id' => 'Mikrotik tidak valid untuk tenant aktif.']);
        }

        return DB::transaction(function () use ($data, $actor, $tenantId, $package) {
            $agent = $actor->access_level === UserAccessLevel::CUSTOMER
                ? VoucherAgent::with('user')->where('tenant_id', $tenantId)->where('user_id', $actor->id)->where('status', 'active')->lockForUpdate()->first()
                : null;
            if ($actor->access_level === UserAccessLevel::CUSTOMER && ! $agent) {
                throw ValidationException::withMessages(['agent' => 'Akun belum terdaftar sebagai agen voucher aktif.']);
            }

            $quantity = (int) $data['quantity'];
            $gross = (float) $package->price * $quantity;
            $benefitPercent = $agent?->price_mode === 'commission'
                ? (float) $agent->commission_percent
                : (float) ($agent?->discount_percent ?? 0);
            $discount = round($gross * $benefitPercent / 100, 2);
            $total = $gross - $discount;
            $saleUuid = (string) Str::uuid();

            if ($agent?->payment_type === 'prepaid') {
                $this->wallets->chargeUser($actor, $tenantId, $total, "voucher-sale:{$saleUuid}", "Pembelian {$quantity} voucher {$package->name}");
            }
            if ($agent?->payment_type === 'postpaid') {
                if ((float) $agent->outstanding_balance + $total > (float) $agent->credit_limit) {
                    throw ValidationException::withMessages(['credit_limit' => 'Limit kredit agen tidak mencukupi.']);
                }
                $agent->increment('outstanding_balance', $total);
            }

            $sale = VoucherSale::create([
                'uuid' => $saleUuid, 'sale_number' => $this->nextSaleNumber($tenantId), 'tenant_id' => $tenantId,
                'voucher_agent_id' => $agent?->id, 'nas_package_profile_id' => $package->id,
                'created_by' => $actor->id, 'source' => 'dashboard', 'quantity' => $quantity,
                'unit_price' => $package->price, 'discount_amount' => $discount, 'total_amount' => $total,
                'payment_status' => $agent?->payment_type === 'postpaid' ? 'credit' : 'paid', 'status' => 'completed',
                'pricing_snapshot' => [
                    'agent_type' => $agent?->payment_type, 'price_mode' => $agent?->price_mode,
                    'discount_percent' => $agent?->discount_percent, 'commission_percent' => $agent?->commission_percent,
                    'agent_benefit_amount' => $discount,
                ],
                'notes' => $data['notes'] ?? null,
            ]);

            for ($index = 0; $index < $quantity; $index++) {
                $username = $this->uniqueVoucherCode($tenantId);
                $password = Str::upper(Str::random(6));
                $voucher = HotspotVoucher::create([
                    'uuid' => Str::uuid(), 'tenant_id' => $tenantId, 'voucher_sale_id' => $sale->id,
                    'nas_package_profile_id' => $package->id, 'nas_mikrotik_id' => $data['nas_mikrotik_id'] ?? null,
                    'username' => $username, 'password' => $password, 'status' => 'generated', 'sync_status' => 'pending',
                    'profile_snapshot' => ['name' => $package->name, 'external_profile_name' => $package->external_profile_name, 'session_timeout' => $package->session_timeout],
                ]);
                $voucher->setAttribute('access_password', $password);
            }

            return $this->saleDetail($sale, $actor, $tenantId);
        });
    }

    public function agents(array $filters, User $actor, ?int $tenantId)
    {
        return VoucherAgent::with(['user:id,name,username,email,whatsapp_number'])
            ->when(! $actor->isMasterAdmin(), fn ($q) => $q->where('tenant_id', $tenantId))
            ->when($actor->isMasterAdmin() && $tenantId, fn ($q) => $q->where('tenant_id', $tenantId))
            ->when($filters['search'] ?? null, fn ($q, $search) => $q->where(fn ($x) => $x->where('agent_code', 'ilike', "%{$search}%")->orWhereHas('user', fn ($u) => $u->where('name', 'ilike', "%{$search}%"))))
            ->latest('id')->paginate($filters['per_page'] ?? 10);
    }

    public function saveAgent(array $data, User $actor, ?int $tenantId, ?VoucherAgent $agent = null): VoucherAgent
    {
        $effectiveTenant = $actor->isMasterAdmin() ? ($data['tenant_id'] ?? $tenantId ?? $agent?->tenant_id) : $tenantId;
        if (! $effectiveTenant) {
            throw ValidationException::withMessages(['tenant_id' => 'Tenant wajib dipilih.']);
        }
        if ($agent) {
            $this->authorizeTenant($agent->tenant_id, $effectiveTenant, $actor);
        }
        $belongs = DB::table('user_tenants')->where('tenant_id', $effectiveTenant)->where('user_id', $data['user_id'])->exists();
        if (! $belongs) {
            throw ValidationException::withMessages(['user_id' => 'User tidak terdaftar pada tenant tersebut.']);
        }

        $payload = [...$data, 'tenant_id' => $effectiveTenant, 'uuid' => $agent?->uuid ?? Str::uuid()];
        $payload['agent_code'] = filled($data['agent_code'] ?? null) ? Str::upper($data['agent_code']) : $this->nextAgentCode($effectiveTenant);
        if ($data['payment_type'] === 'prepaid') {
            $payload['credit_limit'] = 0;
        }
        $model = $agent ?: new VoucherAgent;
        $model->fill($payload)->save();

        return $model->load('user:id,name,username,email,whatsapp_number');
    }

    public function options(string $scope, User $actor, ?int $tenantId): array
    {
        $data = [];
        if (in_array($scope, ['sales', 'login-page'], true)) {
            $data['packages'] = NasPackageProfile::where('tenant_id', $tenantId)->where('service_type', 'hotspot')->where('status', 'active')->orderBy('name')->get();
        }
        if ($scope === 'sales') {
            $data['mikrotiks'] = NasMikrotik::where('tenant_id', $tenantId)->where('status', 'active')->orderBy('name')->get(['id', 'name', 'connection_type']);
        }
        if ($scope === 'agents') {
            $data['users'] = User::whereHas('userTenants', fn ($q) => $q->where('tenant_id', $tenantId))->orderBy('name')->get(['id', 'name', 'username', 'email']);
        }

        return $data;
    }

    public function loginPage(User $actor, ?int $tenantId): array
    {
        if (! $tenantId) {
            throw ValidationException::withMessages(['tenant_id' => 'Tenant aktif wajib tersedia.']);
        }
        $setting = VoucherLoginPageSetting::firstOrCreate(['tenant_id' => $tenantId], [
            'uuid' => Str::uuid(), 'public_key' => Str::random(64), 'enabled' => false,
        ]);

        return ['setting' => $setting->load('gatewayAccount.provider:id,code,name'), 'embed_script' => $this->embedScript($setting), 'dummy_sales' => $setting->enabled ? $this->dummySales() : []];
    }

    public function saveLoginPage(array $data, User $actor, ?int $tenantId): array
    {
        $setting = VoucherLoginPageSetting::firstOrCreate(['tenant_id' => $tenantId], ['uuid' => Str::uuid(), 'public_key' => Str::random(64)]);
        $requestedPackages = collect($data['allowed_package_profile_ids'] ?? [])->map(fn ($id) => (int) $id)->unique();
        $validPackageCount = NasPackageProfile::where('tenant_id', $tenantId)->where('service_type', 'hotspot')->whereIn('id', $requestedPackages)->count();
        if ($validPackageCount !== $requestedPackages->count()) {
            throw ValidationException::withMessages(['allowed_package_profile_ids' => 'Terdapat profile paket yang bukan milik tenant aktif.']);
        }
        if ($data['enabled']) {
            $paymentSetting = TenantPaymentSetting::where('tenant_id', $tenantId)->whereIn('payment_mode', ['tenant_gateway', 'platform_gateway'])->first();
            $accountId = $paymentSetting?->payment_mode === 'tenant_gateway' ? $paymentSetting?->tenant_gateway_account_id : $paymentSetting?->platform_gateway_account_id;
            $gateway = PaymentGatewayAccount::whereKey($accountId)->where('enabled', true)->first();
            if (! $gateway) {
                throw ValidationException::withMessages(['enabled' => 'LoginPage Voucher hanya dapat diaktifkan jika tenant memiliki payment gateway aktif.']);
            }
            $data['payment_gateway_account_id'] = $gateway->id;
        } else {
            $data['payment_gateway_account_id'] = null;
        }
        $setting->update([...$data, 'enabled_at' => $data['enabled'] ? ($setting->enabled_at ?? now()) : null]);

        return $this->loginPage($actor, $tenantId);
    }

    private function embedScript(VoucherLoginPageSetting $setting): string
    {
        return '<div id="voucher-container"></div>'."\n".'<script src="https://manjapro.net/voucherpay/cl-style.js?i='.$setting->public_key.'"></script>';
    }

    private function dummySales(): array
    {
        return collect(range(1, 5))->map(fn ($i) => [
            'id' => $i, 'reference' => 'DEMO-'.now()->format('Ymd').'-'.str_pad((string) $i, 3, '0', STR_PAD_LEFT),
            'package' => ['Hotspot 2 Jam', 'Hotspot Harian', 'Hotspot Mingguan'][$i % 3],
            'buyer' => 'Customer Demo '.$i, 'amount' => 5000 * $i,
            'status' => $i === 4 ? 'pending' : 'paid', 'created_at' => now()->subMinutes($i * 17)->toIso8601String(),
        ])->all();
    }

    private function nextSaleNumber(int $tenantId): string
    {
        return 'VCR-'.$tenantId.'-'.now()->format('YmdHis').'-'.Str::upper(Str::random(4));
    }

    private function nextAgentCode(int $tenantId): string
    {
        return 'AGT-'.$tenantId.'-'.str_pad((string) (VoucherAgent::where('tenant_id', $tenantId)->count() + 1), 4, '0', STR_PAD_LEFT);
    }

    private function uniqueVoucherCode(int $tenantId): string
    {
        do {
            $code = 'V'.Str::upper(Str::random(9));
        } while (HotspotVoucher::where('tenant_id', $tenantId)->where('username', $code)->exists());

        return $code;
    }

    private function authorizeTenant(int $modelTenantId, ?int $tenantId, User $actor): void
    {
        if (! $actor->isMasterAdmin()) {
            abort_unless($tenantId && $modelTenantId === $tenantId, 403);
        }
    }
}
