<?php

namespace App\Services\Integration;

use App\Models\AdminSite;
use App\Models\AdminRole;
use App\Models\AdminUser;
use App\Models\Player;
use App\Support\SitePlatformRole;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use Illuminate\Support\Facades\DB;

final class IntegrationSiteService
{
    public function __construct(
        private readonly PartnerSiteConfigResolver $configResolver,
    ) {}

    /**
     * @param  array<string, mixed>  $data
     * @return array{site: AdminSite, secrets: array{sso_jwt_secret: string, wallet_api_key: string}, admin_user: AdminUser}
     */
    public function create(array $data): array
    {
        $secrets = $this->generateSecrets();

        ['site' => $site, 'admin_user' => $adminUser] = DB::transaction(function () use ($data, $secrets): array {
            /** @var array{username: string, nickname: string, password: string, email?: string|null} $adminAccount */
            $adminAccount = $data['admin_account'];

            $site = AdminSite::query()->create([
                'code' => (string) $data['code'],
                'name' => (string) $data['name'],
                'currency_code' => (string) ($data['currency_code'] ?? 'NPR'),
                'status' => (int) ($data['status'] ?? 1),
                'is_default' => false,
                'wallet_api_url' => $this->nullableTrim($data['wallet_api_url'] ?? null),
                'wallet_debit_path' => (string) ($data['wallet_debit_path'] ?? '/wallet/debit-for-lottery'),
                'wallet_credit_path' => (string) ($data['wallet_credit_path'] ?? '/wallet/credit-from-lottery'),
                'wallet_balance_path' => (string) ($data['wallet_balance_path'] ?? '/wallet/balance'),
                'wallet_timeout_seconds' => max(1, (int) ($data['wallet_timeout_seconds'] ?? 10)),
                'iframe_allowed_origins' => $data['iframe_allowed_origins'] ?? null,
                'lottery_h5_base_url' => $this->nullableTrim($data['lottery_h5_base_url'] ?? null),
                'notes' => $this->nullableTrim($data['notes'] ?? null),
                'sso_jwt_secret_encrypted' => encrypt($secrets['sso_jwt_secret']),
                'wallet_api_key_encrypted' => encrypt($secrets['wallet_api_key']),
            ]);

            $role = SitePlatformRole::resolve();
            $adminUser = $this->createSiteAdminUser($site, $role, $adminAccount);

            return [
                'site' => $site,
                'admin_user' => $adminUser,
            ];
        });

        $this->configResolver->forgetCache((string) $site->code);

        return [
            'site' => $site->fresh(),
            'secrets' => $secrets,
            'admin_user' => $adminUser->fresh(),
        ];
    }

    /**
     * @param  array<string, mixed>  $data
     */
    public function update(AdminSite $site, array $data): AdminSite
    {
        $site->fill([
            'name' => (string) $data['name'],
            'currency_code' => (string) ($data['currency_code'] ?? $site->currency_code),
            'status' => (int) ($data['status'] ?? $site->status),
            'wallet_api_url' => array_key_exists('wallet_api_url', $data)
                ? $this->nullableTrim($data['wallet_api_url'])
                : $site->wallet_api_url,
            'wallet_debit_path' => (string) ($data['wallet_debit_path'] ?? $site->wallet_debit_path),
            'wallet_credit_path' => (string) ($data['wallet_credit_path'] ?? $site->wallet_credit_path),
            'wallet_balance_path' => (string) ($data['wallet_balance_path'] ?? $site->wallet_balance_path),
            'wallet_timeout_seconds' => max(1, (int) ($data['wallet_timeout_seconds'] ?? $site->wallet_timeout_seconds)),
            'iframe_allowed_origins' => $data['iframe_allowed_origins'] ?? $site->iframe_allowed_origins,
            'lottery_h5_base_url' => array_key_exists('lottery_h5_base_url', $data)
                ? $this->nullableTrim($data['lottery_h5_base_url'])
                : $site->lottery_h5_base_url,
            'notes' => array_key_exists('notes', $data)
                ? $this->nullableTrim($data['notes'])
                : $site->notes,
        ]);
        $site->save();

        $this->configResolver->forgetCache((string) $site->code);

        return $site->fresh();
    }

    /**
     * @return array{site: AdminSite, secrets: array{sso_jwt_secret: string, wallet_api_key: string}}
     */
    public function destroy(AdminSite $site): void
    {
        $siteCode = (string) $site->code;
        $siteId = (int) $site->id;
        $siteAdminRoleId = SitePlatformRole::resolve()->id;

        if (AdminSite::query()->count() <= 1) {
            $fallbackSiteId = null;
        } else {
            $fallbackSiteId = (int) AdminSite::query()
                ->where('id', '!=', $siteId)
                ->orderBy('id')
                ->value('id');
        }

        DB::transaction(function () use ($site, $siteCode, $siteId, $siteAdminRoleId, $fallbackSiteId): void {
            $superRoleId = AdminRole::query()
                ->where('slug', AdminUser::ROLE_SUPER_ADMIN)
                ->value('id');

            $platformBindings = DB::table('admin_user_site_roles')
                ->where('site_id', $siteId)
                ->when($siteAdminRoleId !== null, static fn ($query) => $query->where('role_id', '!=', $siteAdminRoleId))
                ->when($superRoleId !== null, static fn ($query) => $query->where('role_id', '!=', $superRoleId))
                ->get(['admin_user_id', 'role_id', 'granted_at']);

            foreach ($platformBindings as $binding) {
                if ($fallbackSiteId === null) {
                    continue;
                }

                DB::table('admin_user_site_roles')->updateOrInsert(
                    [
                        'admin_user_id' => (int) $binding->admin_user_id,
                        'site_id' => $fallbackSiteId,
                        'role_id' => (int) $binding->role_id,
                    ],
                    ['granted_at' => $binding->granted_at ?? now()],
                );
            }

            Player::query()->where('site_code', $siteCode)->delete();

            if ($siteAdminRoleId !== null) {
                $siteAdminUserIds = DB::table('admin_user_site_roles')
                    ->where('site_id', $siteId)
                    ->where('role_id', $siteAdminRoleId)
                    ->pluck('admin_user_id');

                foreach ($siteAdminUserIds as $userId) {
                    $bindings = DB::table('admin_user_site_roles')
                        ->where('admin_user_id', $userId)
                        ->get(['site_id', 'role_id']);

                    $onlyAutoSiteAdmin = $bindings->count() === 1
                        && (int) $bindings[0]->site_id === $siteId
                        && (int) $bindings[0]->role_id === (int) $siteAdminRoleId;

                    if ($onlyAutoSiteAdmin) {
                        AdminUser::query()->where('id', $userId)->delete();
                    }
                }
            }

            $site->delete();
        });

        $this->configResolver->forgetCache($siteCode);
    }

    /**
     * @return array{site: AdminSite, secrets: array{sso_jwt_secret: string, wallet_api_key: string}}
     */
    public function rotateSecrets(AdminSite $site): array
    {
        $secrets = $this->generateSecrets();

        $site->forceFill([
            'sso_jwt_secret_encrypted' => encrypt($secrets['sso_jwt_secret']),
            'wallet_api_key_encrypted' => encrypt($secrets['wallet_api_key']),
        ])->save();

        $this->configResolver->forgetCache((string) $site->code);

        return ['site' => $site->fresh(), 'secrets' => $secrets];
    }

    /**
     * @return array{sso_jwt_secret: string, wallet_api_key: string}
     */
    private function generateSecrets(): array
    {
        return [
            'sso_jwt_secret' => Str::random(48),
            'wallet_api_key' => Str::random(40),
        ];
    }

    private function nullableTrim(mixed $value): ?string
    {
        if (! is_string($value)) {
            return null;
        }

        $trimmed = trim($value);

        return $trimmed === '' ? null : $trimmed;
    }

    /**
     * @param  array{username: string, nickname: string, password: string, email?: string|null}  $adminAccount
     */
    private function createSiteAdminUser(AdminSite $site, AdminRole $role, array $adminAccount): AdminUser
    {
        $username = trim((string) ($adminAccount['username'] ?? ''));
        $nickname = trim((string) ($adminAccount['nickname'] ?? ''));
        $password = (string) ($adminAccount['password'] ?? '');
        $email = $this->nullableTrim($adminAccount['email'] ?? null);

        if ($username === '' || $nickname === '' || $password === '') {
            throw ValidationException::withMessages([
                'admin_account' => ['站点后台管理账号信息不完整。'],
            ]);
        }

        $user = AdminUser::query()->create([
            'username' => $username,
            'name' => $nickname,
            'email' => $email,
            'password' => $password,
            'status' => 0,
        ]);

        $user->syncSystemRoleSlugsForSite((int) $site->id, [SitePlatformRole::SLUG]);

        return $user;
    }
}
