### ----------------------------------------------------
### -- MUGS::Core
### -- Licenses: Artistic-2.0
### -- Authors: Geoffrey Broadwell
### -- File: lib/MUGS/Server.rakumod
### ----------------------------------------------------
### -- Chunk 2 of 2
 create-account-owner(::?CLASS:D: Str:D :$username!, Str:D :$password!) {
        my $credential = MUGS::Authentication::Password.new(:$password);
        self.create-account-owner(:$username, :$credential);
    }

    method authenticate-user(::?CLASS:D: :$username!, :$session!, :$password!) {
        my $credentials = $!credential-store.credentials-for-username($username, :auth-type<password>);

        if $credentials.first(*.verify($password)) {
            MUGS::Server::LogTimelineSchema::UserAuthSuccess.log(:$username, :session-id($session.id));
            $!identity-store.user-by-name($username);
        }
        else {
            MUGS::Server::LogTimelineSchema::UserAuthFailure.log(:$username, :session-id($session.id));
            X::MUGS::Server::InvalidCredentials.new(:$username).throw;
        }
    }

    method get-info-bundle(::?CLASS:D: MUGS::User:D :$user!, :%info-types!) {
        my %info;

        for < available-identities available-game-types active-games > {
            %info{$_} := self."get-info-$_"($user) if %info-types{$_};
        }

        %info
    }

    method get-info-available-identities(::?CLASS:D: MUGS::User:D $user) {
        my @personas = $user.available-personas.map: {
            my @characters = .characters.map(*.screen-name);
            hash(persona => .screen-name, :@characters)
        }
    }

    method get-info-available-game-types(::?CLASS:D: MUGS::User:D $user) {
        # XXXX: Permissions to see game types?
        my @game-types = self.known-implementations.map: {
            my $impl         = self.implementation-class($_);
            my $game-desc    = $impl.game-desc;
            my @genre-tags   = $impl.genre-tags;
            my @content-tags = $impl.content-tags;
            my @config-form  = $impl.config-form.map: {
                my %field    = .< field section desc default visible-locally >:kv;
                %field<type> = .<type>.^name;
                %field
            };

            hash(game-type => $_, :$game-desc, :@genre-tags, :@content-tags, :@config-form)
        }
    }

    method get-info-active-games(::?CLASS:D: MUGS::User:D $user) {
        my @characters is Set = $user.available-personas.map(*.characters).flat.map(*.screen-name);
        my @active-games = %!game.kv.map: -> GameID() $game-id, $game {
            my $game-type           = $game.game-type;
            my $gamestate           = $game.gamestate;
            my $config              = $game.config;
            my $created-by-me       = $game.creator === $user
                                   || $game.creator.username eq $user.username;
            my $num-participants    = $game.participants.elems;
            my @participants is Set = $game.participants.map(*<character>.screen-name);
            my @my-characters       = (@characters ∩ @participants).keys;

            # Don't include private or finished games that user is not already
            # associated with (the creator or controlling a joined character)
            next unless $created-by-me || @my-characters
                     || !$config<invites-only> && $game.gamestate < Finished;

            # XXXX: Permissions to view games/configs/details?

            hash(:$game-id, :$game-type, :$gamestate, :$config,
                 :$created-by-me, :$num-participants, :@my-characters)
        }
    }

    method create-persona(::?CLASS:D: MUGS::User:D :$creator!, Str:D :$screen-name!) {
        # XXXX: Permission to create a persona
        # XXXX: Detect failure to create persona
        my $account = $creator.account;
        my $persona = $!identity-store.new-persona(:$screen-name, :$account);
        $persona.add-authorized-users($creator);
        # XXXX: Mark user as persona creator
        $persona
    }

    multi method create-character(::?CLASS:D: MUGS::User:D :$creator!,
                                  MUGS::Persona:D :$persona!,
                                  Str:D :$screen-name!) {
        # XXXX: Permission to use persona and create a character
        # XXXX: Detect failure to create character
        my $character = $!identity-store.new-character(:$screen-name, :$persona);
        # XXXX: Mark user as character creator
        $character
    }

    multi method create-character(::?CLASS:D: MUGS::User:D :$creator!,
                                  Str:D :$persona-name!,
                                  Str:D :$screen-name!) {
        # XXXX: Detect failure to load persona
        my $persona = $!identity-store.persona-by-name($persona-name);
        self.create-character(:$creator, :$persona, :$screen-name);
    }

    method new-game(::?CLASS:D: MUGS::User:D :$creator!, Str:D :$game-type!, :%config!) {
        self!ensure-game-type-exists($game-type);
        my $game = self.implementation-class($game-type).new(:$creator, :%config, :server(self));
        $game.start-dispatcher;
        %!game{$game.id} = $game;
        MUGS::Server::LogTimelineSchema::GameCreated.log(:$game-type, :game-id($game.id), :creator($creator.username));
        $game.id;
    }

    method broadcast-to-sessions(::?CLASS:D: :$message!) {
        for %!session.values -> $session {
            $session.push-message(:$message, :message-type<broadcast>,
                                  :target<active-session>, :target-type<session>);
        }
    }
}


