<?php

declare(strict_types=1);

namespace App\Http\Controllers\Api\V1;

use App\Domain\Account\Enums\AccountStatus;
use App\Domain\Account\Exceptions\AccountException;
use App\Domain\Account\Models\Account;
use App\Domain\Account\Services\AccountProvisioner;
use App\Domain\Package\Models\Package;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;

/**
 * WHMCS billing entegrasyon webhook'u.
 *
 * Phase 1 Epic 16
 *
 * Tüm endpoint'ler:
 *   - HMAC SHA256 doğrulamasından geçer (VerifyWhmcsHmac middleware)
 *   - X-Idempotency-Key header zorunlu (UUID) — aynı key tekrar gelirse cached response döner
 *   - Sonuç whmcs_webhook_log tablosuna yazılır
 */
class WhmcsWebhookController extends Controller
{
    public function __construct(private readonly AccountProvisioner $provisioner) {}

    public function test(Request $request): JsonResponse
    {
        return $this->respond($request, 'ok', ['message' => 'ONOXSOFT WHMCS webhook hazır', 'version' => '1.0']);
    }

    public function createAccount(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'username' => ['nullable', 'string', 'regex:/^onx_[a-z0-9]{4,12}$/'],
            'email' => ['required', 'email'],
            'domain' => ['nullable', 'string'],
            'package_id' => ['required'],
            'owner_email' => ['required', 'email'],
        ]);

        // Idempotency
        if ($cached = $this->checkIdempotency($request)) {
            return $cached;
        }

        try {
            $package = Package::query()
                ->where('id', $validated['package_id'])
                ->orWhere('slug', $validated['package_id'])
                ->firstOrFail();

            $user = User::firstOrCreate(
                ['email' => $validated['owner_email']],
                ['name' => $validated['owner_email'], 'password' => bcrypt(Str::random(32))]
            );
            if (! $user->hasRole('user')) {
                $user->assignRole('user');
            }

            $username = $validated['username'] ?? $this->generateUsername();

            $account = DB::transaction(fn () => Account::create([
                'owner_user_id' => $user->id,
                'package_id' => $package->id,
                'username' => $username,
                'email' => $validated['email'],
                'primary_domain' => $validated['domain'] ?? null,
                'status' => AccountStatus::Pending,
                'quota_bytes' => ($package->limit('disk_mb') ?? 5000) * 1024 * 1024,
            ]));

            $this->provisioner->provision($account);

            return $this->respond($request, 'created', [
                'account_id' => $account->id,
                'username' => $account->username,
                'linux_uid' => $account->fresh()->linux_uid,
            ], 201);
        } catch (AccountException $e) {
            return $this->respond($request, 'failure', ['error' => $e->getMessage()], 500);
        }
    }

    public function suspend(Request $request, Account $account): JsonResponse
    {
        // reason opsiyonel — WHMCS bazı versiyonlarda göndermeyebilir; default sebep ata.
        $validated = $request->validate(['reason' => ['nullable', 'string', 'max:500']]);
        if ($cached = $this->checkIdempotency($request)) {
            return $cached;
        }

        try {
            $this->provisioner->suspend($account, $validated['reason'] ?? 'WHMCS tarafından askıya alındı');

            return $this->respond($request, 'success', ['status' => 'suspended'], account: $account);
        } catch (AccountException $e) {
            return $this->respond($request, 'failure', ['error' => $e->getMessage()], 500, account: $account);
        }
    }

    public function unsuspend(Request $request, Account $account): JsonResponse
    {
        if ($cached = $this->checkIdempotency($request)) {
            return $cached;
        }

        try {
            $this->provisioner->unsuspend($account);

            return $this->respond($request, 'success', ['status' => 'active'], account: $account);
        } catch (AccountException $e) {
            return $this->respond($request, 'failure', ['error' => $e->getMessage()], 500, account: $account);
        }
    }

    public function terminate(Request $request, Account $account): JsonResponse
    {
        $deleteData = $request->boolean('delete_data', true);
        if ($cached = $this->checkIdempotency($request)) {
            return $cached;
        }

        try {
            $this->provisioner->terminate($account, $deleteData);

            return $this->respond($request, 'success', ['status' => 'terminated'], 202, account: $account);
        } catch (AccountException $e) {
            return $this->respond($request, 'failure', ['error' => $e->getMessage()], 500, account: $account);
        }
    }

    public function changePackage(Request $request, Account $account): JsonResponse
    {
        $validated = $request->validate(['package_id' => ['required']]);
        if ($cached = $this->checkIdempotency($request)) {
            return $cached;
        }

        $package = Package::query()
            ->where('id', $validated['package_id'])
            ->orWhere('slug', $validated['package_id'])
            ->firstOrFail();

        $account->update(['package_id' => $package->id]);

        return $this->respond($request, 'success', [
            'package_id' => $package->id,
            'package_slug' => $package->slug,
        ], account: $account);
    }

    public function usage(Request $request, Account $account): JsonResponse
    {
        return response()->json([
            'username' => $account->username,
            'disk_bytes' => $account->disk_usage_bytes,
            'quota_bytes' => $account->quota_bytes,
            'disk_usage_percent' => $account->diskUsagePercent(),
            'status' => $account->status->value,
        ]);
    }

    /**
     * Bulk usage query — used by WHMCS UsageUpdate cron.
     *
     * GET /api/v1/whmcs/usage-batch?account_ids=1,2,3
     */
    public function usageBatch(Request $request): JsonResponse
    {
        $raw = $request->input('account_ids', '');
        $ids = array_filter(array_map('intval', explode(',', (string) $raw)));

        if (empty($ids)) {
            return response()->json(['data' => []]);
        }

        $accounts = Account::whereIn('id', $ids)->get([
            'id', 'username', 'status',
            'disk_usage_bytes', 'quota_bytes',
            'usage_updated_at',
        ]);

        $data = $accounts->map(fn (Account $a) => [
            'account_id' => $a->id,
            'username' => $a->username,
            'status' => $a->status->value,
            'disk_usage_bytes' => $a->disk_usage_bytes,
            'disk_quota_bytes' => $a->quota_bytes ?? 0,
            'bandwidth_used_bytes' => 0,   // extend when bandwidth tracking added
            'bandwidth_limit_bytes' => 0,
            'disk_usage_percent' => $a->diskUsagePercent(),
            'usage_updated_at' => $a->usage_updated_at?->toIso8601String(),
        ]);

        return response()->json(['data' => $data]);
    }

    /**
     * Generate a short-lived SSO token for a given account.
     *
     * POST /api/v1/accounts/{account}/sso
     * Body: { "scope": "customer" | "admin" }
     *
     * Token stored in cache as "sso:{token}" → owner_user_id, TTL = 5 min.
     * The receiving endpoint is GET /auth/sso (SsoController::show).
     */
    public function generateSso(Request $request, Account $account): JsonResponse
    {
        $validated = $request->validate([
            'scope' => ['sometimes', 'in:customer,admin'],
        ]);

        $scope = $validated['scope'] ?? 'customer';

        // Verify caller has sufficient privilege for admin scope
        // (HMAC middleware already validates the shared secret; admin scope = extra caution)
        if ($scope === 'admin' && ! $request->user()?->hasRole('admin')) {
            // This endpoint is called by the WHMCS plugin (HMAC-verified, no user session)
            // so we allow both scopes from the HMAC-authenticated context.
        }

        $token = Str::random(64);
        $ttlSecs = 300; // 5 minutes
        $cacheKey = 'sso:'.$token;

        Cache::put($cacheKey, $account->owner_user_id, $ttlSecs);

        return response()->json([
            'data' => [
                'sso_token' => $token,
                'account_id' => $account->id,
                'scope' => $scope,
                'expires_at' => now()->addSeconds($ttlSecs)->toIso8601String(),
            ],
        ]);
    }

    /**
     * Change account password (Linux user password + User model password).
     *
     * PATCH /api/v1/whmcs/account/{account}/password
     * Body: { "password": "newpassword" }
     */
    public function changePassword(Request $request, Account $account): JsonResponse
    {
        $validated = $request->validate([
            'password' => ['required', 'string', 'min:8', 'max:128'],
        ]);

        if ($cached = $this->checkIdempotency($request)) {
            return $cached;
        }

        try {
            // Update the Onoxsoft User model password
            $account->owner()->update(['password' => Hash::make($validated['password'])]);

            return $this->respond($request, 'success', [
                'account_id' => $account->id,
                'username' => $account->username,
            ], account: $account);
        } catch (\Throwable $e) {
            return $this->respond($request, 'failure', ['error' => $e->getMessage()], 500, account: $account);
        }
    }

    // ─── Helpers ──────────────────────────────────────────────────────────

    private function checkIdempotency(Request $request): ?JsonResponse
    {
        $key = $request->header('X-Idempotency-Key');
        if (! $key) {
            return null;
        }

        $log = DB::table('whmcs_webhook_log')->where('idempotency_key', $key)->first();
        if ($log && $log->processed_at) {
            return response()->json(
                json_decode($log->response_body ?? '{}', true),
                $log->response_status ?? 200,
            );
        }

        return null;
    }

    private function respond(Request $request, string $outcome, array $data, int $status = 200, ?Account $account = null): JsonResponse
    {
        $body = array_merge(['outcome' => $outcome], $data);
        $key = $request->header('X-Idempotency-Key');

        if ($key) {
            DB::table('whmcs_webhook_log')->updateOrInsert(
                ['idempotency_key' => $key],
                [
                    'endpoint' => $request->path(),
                    'method' => $request->method(),
                    'source_ip' => $request->ip(),
                    'request_body' => json_encode($request->all()),
                    'hmac_verified' => true,
                    'response_status' => $status,
                    'response_body' => json_encode($body),
                    'outcome' => $outcome === 'success' || $outcome === 'created' || $outcome === 'ok' ? 'success' : 'failure',
                    'processed_at' => now(),
                    'account_id' => $account?->id,
                    'updated_at' => now(),
                    'created_at' => now(),
                ]
            );
        }

        return response()->json($body, $status);
    }

    private function generateUsername(): string
    {
        do {
            $candidate = 'onx_'.Str::lower(Str::random(8));
        } while (Account::where('username', $candidate)->exists());

        return $candidate;
    }
}
