package LasTres::Schema::Result::PJ;

use v5.36.0;

use strict;
use warnings;

use feature 'signatures';

use parent 'DBIx::Class::Core';

use UUID::URandom qw/create_uuid_string/;
use List::AllUtils;
use Data::Dumper;

use JSON qw/to_json/;

use Moo;

__PACKAGE__->table('player_pjs');

__PACKAGE__->add_columns(
    uuid => {
        data_type     => 'uuid',
        default_value => \'uuid_generate_v4()',
        is_nullable   => 0,
    },
    owner => {
        data_type      => 'uuid',
        is_nullable    => 0,
        is_foreign_key => 1,
    },
    full_name => {
        data_type   => 'text',
        is_nullable => 0,
    },
    short_name => {
        data_type   => 'text',
        is_nullable => 0,
    },
    nick => {
        data_type   => 'text',
        is_nullable => 0,
    },
    race => {
        data_type   => 'text',
        is_nullable => 0,
        accessor    => "_race",
    },
    team => {
        data_type   => 'uuid',
        is_nullable => 0,
    },
    creation_date => {
        data_type     => 'timestamp',
        default_value => \'NOW()',
        is_nullable   => 0,
    },
    last_activity => {
        data_type     => 'timestamp',
        default_value => \'NOW()',
        is_nullable   => 0,
    },
    experience => {
        data_type     => 'integer',
        default_value => \'1',
        is_nullable   => 0,
    },
    equipment => {
        data_type      => 'uuid',
        is_nullable    => 0,
        is_foreign_key => 1,
    },
    born_stats => {
        data_type      => 'uuid',
        is_foreign_key => 1,
        is_nullable    => 0,
    },
    training_stats => {
        data_type      => 'uuid',
        is_foreign_key => 1,
        is_nullable    => 0,
    },
    skills => {
        data_type      => 'uuid',
        is_nullable    => 0,
        is_foreign_key => 1,
    },
    spells => {
        data_type      => 'uuid',
        is_nullable    => 0,
        is_foreign_key => 1,
    },
    inventory => {
        data_type   => 'uuid',
        is_nullable => 0,
    },
    health => {
        data_type   => 'integer',
        accessor    => '_health',
        is_nullable => 0,
    },
    mana => {
        data_type   => 'integer',
        accessor    => '_mana',
        is_nullable => 0,
    }
);

__PACKAGE__->set_primary_key('uuid');

__PACKAGE__->has_many( 'npcs', 'LasTres::Schema::Result::CompanionNPC',
    'owner' );
__PACKAGE__->has_many( 'logs',  'LasTres::Schema::Result::PJLog',  'owner' );
__PACKAGE__->has_many( 'flags', 'LasTres::Schema::Result::PJFlag', 'owner' );
__PACKAGE__->belongs_to( 'born_stats',     'LasTres::Schema::Result::Stats' );
__PACKAGE__->belongs_to( 'training_stats', 'LasTres::Schema::Result::Stats' );
__PACKAGE__->belongs_to( 'inventory', 'LasTres::Schema::Result::Inventory' );
__PACKAGE__->belongs_to( 'skills', 'LasTres::Schema::Result::SkillLikeList' );
__PACKAGE__->belongs_to( 'spells', 'LasTres::Schema::Result::SkillLikeList' );
__PACKAGE__->belongs_to( 'equipment', 'LasTres::Schema::Result::Equipment' );
__PACKAGE__->belongs_to( 'team',      'LasTres::Schema::Result::Team' );
__PACKAGE__->belongs_to( 'owner',     'LasTres::Schema::Result::Player' );

sub hash ($self) {
    my $image;
    my $race = $self->race;
    if ( $race->can('image') ) {
        $image = $race->image;
    }
    return {
        uuid       => $self->uuid,
        full_name  => $self->full_name,
        short_name => $self->short_name,
        nick       => $self->nick,
        race       => $self->race,
        health     => $self->health,
        max_health => $self->max_health,
        mana       => $self->mana,
        max_mana   => $self->max_mana,
        (
              ( defined $image )
            ? ( image => $image )
            : ()
        ),
    };
}

my $columns = __PACKAGE__->columns_info;
for my $column_name ( keys %$columns ) {
    my $column      = $columns->{$column_name};
    my $is_nullable = $column->{is_nullable};
    $is_nullable //= 0;
    my $required = !$is_nullable;
    if ( defined $column->{default_value} ) {
        $required = 0;
    }
    has $column_name => (
        is       => 'rw',
        required => $required,
        accessor => "_moo_$column_name",
    );
}

sub max_health ($self) {
    my $races            = LasTres::Races->new;
    my $race             = $self->race;
    my $health_base_race = $race->base_stats->health;
    my $health_born      = $self->born_stats->health;
    my $health_training  = $self->training_stats->health;
    my $health_mix =
      2 * $health_base_race + $health_born + ( $health_training / 4 );
    my $health_scaled = ( ( $health_mix * $self->level ) / 100 );
    return int( $health_scaled + $self->level + 10 );
}

sub max_mana ($self) {
    my $races          = LasTres::Races->new;
    my $race           = $self->race;
    my $mana_base_race = $race->base_stats->mana;
    my $mana_born      = $self->born_stats->mana;
    my $mana_training  = $self->training_stats->mana;
    my $mana_mix    = 2 * $mana_base_race + $mana_born + ( $mana_training / 4 );
    my $mana_scaled = ( ( $mana_mix * $self->level ) / 100 );
    return int( $mana_scaled + $self->level + 10 );
}

sub health {
    my $self          = shift;
    my $health_to_set = shift;
    require LasTres::Schema;
    my $schema = LasTres::Schema->Schema;
    $schema->txn_do(
        sub {
            if ( defined $health_to_set ) {
                $self->_health($health_to_set);
                $self->update;
            }
            my $health = $self->_health;
            if ( $health < 0 ) {
                $self->_health(0);
                $self->update;
            }
            if ( $health > $self->max_health ) {
                $self->_health( $self->max_health );
                $self->update;
            }
        }
    );
    return $self->_health;
}

sub mana {
    my $self        = shift;
    my $mana_to_set = shift;
    require LasTres::Schema;
    my $schema = LasTres::Schema->Schema;
    $schema->txn_do(
        sub {
            if ( defined $mana_to_set ) {
                $self->_mana($mana_to_set);
                $self->update;
            }
            my $mana = $self->_mana;
            if ( $mana < 0 ) {
                $self->_mana(0);
                $self->update;
            }
            if ( $mana > $self->max_mana ) {
                $self->_mana( $self->max_mana );
                $self->update;
            }
        }
    );
    return $self->_mana;
}

sub race ($self) {
    my $hash = LasTres::Races->new->hash_playable;
    my $race = $hash->{ $self->_race };
    if ( !defined $race ) {
        die "Not valid race for pj " . $self->uuid;
    }
    return $race;
}

sub last_50_log ($self) {
    return $self->logs->search( {},
        { limit => 50, order_by => { -desc => 'date' } } );
}

sub append_log_line ( $self, $content ) {
    require LasTres::Schema;
    require LasTres::Redis;
    my $redis = LasTres::Redis->new;
    if ( ref $content ne 'ARRAY' ) {
        die 'Bad log content, not a arrayref.';
    }
    for my $section (@$content) {
        if ( ref $section ne 'HASH' ) {
            die 'Invalid section, not a hashref.';
        }
        my @recognized_log_keys = qw/color background text/;
        if (
            List::AllUtils::any {
                my $key = $_;
                (
                    List::AllUtils::none {
                        $key eq $_
                    }
                    @recognized_log_keys
                )
            }
            keys %$section
          )
        {
            die 'The section '
              . ( Data::Dumper::Dumper $section)
              . ' has an unrecognized key';
        }
        if ( !defined $section->{text} ) {
            die 'The section has no text.';
        }
    }
    my $uuid = create_uuid_string;
    my $log =
      LasTres::Schema->Schema->resultset('PJLog')
      ->new(
        { uuid => $uuid, owner => $self->uuid, content => to_json($content) } );
    $log->insert;
    $log = $log->get_from_storage;

    $redis->publish( $redis->pj_subscription($self),
        to_json( { command => 'append-log', log => $log->hash } ) );
}

sub location ($self) {
    return $self->team->location;
}

sub level ($self) {
    return $self->experience**( 1 / 3 );
}

sub set_flag ( $self, $name ) {
    require LasTres::Schema;
    my $schema           = LasTres::Schema->Schema;
    my $result_set_flags = $schema->resultset('PJFlag');
    my $flag = $result_set_flags->new( { name => $name, owner => $self->uuid } )
      ->update_or_insert;
}

sub get_flag ( $self, $name ) {
    my @flags = $self->flags->search( { name => $name } );
    if ( scalar @flags ) {
        return 1;
    }
    return 0;
}

sub clear_flag ( $self, $name ) {
    $self->flags->search( name => $name )->delete;
}
1;
