<?php

declare(strict_types=1);

trait MyronIdentityRegistrationPolicyTrait
{
    public function getRegistrationSettings(): array
    {
        $statement = $this->pdo->query('SELECT * FROM registration_settings WHERE id = 1 LIMIT 1');
        $settings = $statement->fetch();
        $domains = [];
        if (is_array($settings)) {
            $domains = $this->decodeAllowedDomains((string) ($settings['allowed_domains_json'] ?? '[]'));
        }

        return [
            'allowed_domains' => $domains,
            'registrations_enabled' => $domains !== [],
        ];
    }

    public function saveRegistrationSettings(array $domains): array
    {
        $normalized = $this->normalizeAllowedDomains($domains);
        $statement = $this->pdo->prepare(
            'UPDATE registration_settings
             SET allowed_domains_json = :allowed_domains_json,
                 updated_at = :updated_at
             WHERE id = 1'
        );
        $statement->execute([
            ':allowed_domains_json' => json_encode($normalized, JSON_THROW_ON_ERROR),
            ':updated_at' => myronNow(),
        ]);

        return $this->getRegistrationSettings();
    }

    public function registrationsEnabled(): bool
    {
        return $this->getRegistrationSettings()['registrations_enabled'] === true;
    }

    public function createPendingRegistration(array $input): array
    {
        $settings = $this->getRegistrationSettings();
        $allowedDomains = is_array($settings['allowed_domains'] ?? null) ? $settings['allowed_domains'] : [];
        if ($allowedDomains === []) {
            throw new RuntimeException('Registration is currently closed.');
        }

        $email = myronNormalizeEmail((string) ($input['email'] ?? ''));
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new InvalidArgumentException('A valid email address is required.');
        }

        if (!$this->emailDomainAllowed($email, $allowedDomains)) {
            throw new InvalidArgumentException('That email domain is not allowed for registration.');
        }

        $displayName = trim((string) ($input['display_name'] ?? ''));
        if ($displayName === '') {
            throw new InvalidArgumentException('Display Name is required.');
        }

        $password = (string) ($input['password'] ?? '');
        $this->assertPasswordPolicy($password);
        $this->assertEmailAvailable($email);

        $firstName = trim((string) ($input['first_name'] ?? ''));
        $lastName = trim((string) ($input['last_name'] ?? ''));
        $role = $this->requireRoleByKey('viewer');
        $grantedScopesJson = self::encodeGrantedScopes(self::scopesForRoleKey('viewer'));
        $userId = myronGenerateId('user');
        $now = myronNow();

        $statement = $this->pdo->prepare(
            'INSERT INTO users (
                id, username, email, display_name, first_name, last_name, bio, avatar_url,
                facebook_url, x_url, pinterest_url, linkedin_url, youtube_url, instagram_url, tiktok_url,
                password_hash, role_id, granted_scopes_json, is_active, email_verified_at,
                created_at, updated_at, last_login_at
             ) VALUES (
                :id, :username, :email, :display_name, :first_name, :last_name, :bio, :avatar_url,
                :facebook_url, :x_url, :pinterest_url, :linkedin_url, :youtube_url, :instagram_url, :tiktok_url,
                :password_hash, :role_id, :granted_scopes_json, :is_active, :email_verified_at,
                :created_at, :updated_at, :last_login_at
             )'
        );
        $statement->execute([
            ':id' => $userId,
            ':username' => $this->generateUniqueUsername($email),
            ':email' => $email,
            ':display_name' => $displayName,
            ':first_name' => $firstName,
            ':last_name' => $lastName,
            ':bio' => '',
            ':avatar_url' => '',
            ':facebook_url' => '',
            ':x_url' => '',
            ':pinterest_url' => '',
            ':linkedin_url' => '',
            ':youtube_url' => '',
            ':instagram_url' => '',
            ':tiktok_url' => '',
            ':password_hash' => password_hash($password, PASSWORD_DEFAULT),
            ':role_id' => (string) $role['id'],
            ':granted_scopes_json' => $grantedScopesJson,
            ':is_active' => 0,
            ':email_verified_at' => null,
            ':created_at' => $now,
            ':updated_at' => $now,
            ':last_login_at' => null,
        ]);

        $user = $this->findUserById($userId);
        if ($user === null) {
            throw new RuntimeException('Registered user could not be loaded.');
        }

        return $user;
    }

    public function issueRegistrationToken(string $userId): string
    {
        $user = $this->findUserById($userId);
        if ($user === null) {
            throw new RuntimeException('User not found.');
        }

        $this->expireOldRegistrationTokens($userId);

        $selector = myronBase64UrlEncode(random_bytes(9));
        $verifier = myronBase64UrlEncode(random_bytes(24));
        $token = $selector . '.' . $verifier;
        $statement = $this->pdo->prepare(
            'INSERT INTO registration_tokens (
                id, user_id, selector, verifier_hash, requested_at, expires_at, consumed_at
             ) VALUES (
                :id, :user_id, :selector, :verifier_hash, :requested_at, :expires_at, NULL
             )'
        );
        $statement->execute([
            ':id' => myronGenerateId('regtok'),
            ':user_id' => $userId,
            ':selector' => $selector,
            ':verifier_hash' => hash('sha256', $verifier),
            ':requested_at' => myronNow(),
            ':expires_at' => gmdate('Y-m-d H:i:s', time() + 3600),
        ]);

        return $token;
    }

    public function validateRegistrationToken(string $token): ?array
    {
        try {
            [$record, $user] = $this->requireActiveRegistrationToken($token);
            return [
                'token' => $record,
                'user' => $user,
            ];
        } catch (Throwable) {
            return null;
        }
    }

    public function activateRegistration(string $token): array
    {
        [$record, $user] = $this->requireActiveRegistrationToken($token);
        $now = myronNow();

        $statement = $this->pdo->prepare(
            'UPDATE users
             SET is_active = 1,
                 email_verified_at = :email_verified_at,
                 updated_at = :updated_at
             WHERE id = :user_id'
        );
        $statement->execute([
            ':email_verified_at' => $now,
            ':updated_at' => $now,
            ':user_id' => (string) $user['id'],
        ]);

        $consume = $this->pdo->prepare(
            'UPDATE registration_tokens
             SET consumed_at = :consumed_at
             WHERE id = :id'
        );
        $consume->execute([
            ':consumed_at' => $now,
            ':id' => (string) $record['id'],
        ]);

        $activatedUser = $this->findUserById((string) $user['id']);
        if ($activatedUser === null) {
            throw new RuntimeException('Activated user could not be reloaded.');
        }

        return $activatedUser;
    }
}
