package Koha::Objects::Mixin::ExtendedAttributes;

# Copyright 2024 PTFS Europe Ltd
#
# This file is part of Koha.
#
# Koha is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Koha is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Koha; if not, see <http://www.gnu.org/licenses>.

use Modern::Perl;
use Scalar::Util qw( reftype );

=head1 NAME

Koha::Objects::Mixin::ExtendedAttributes

=head2 Class methods


=head3 search

    Overwrites the search method to include extended attributes rewrite and dynamic relation accessors

=cut

sub search {
    my ( $self, $params, $attributes ) = @_;

    my $class = ref($self) ? ref($self) : $self;

    $self->handle_query_extended_attributes(
        {
            attributes      => $attributes,
            filtered_params => $params,
        }
    );

    my $rs = $self->_resultset()->search( $params, $attributes );
    return $class->_new_from_dbic($rs);
}

=head3 handle_query_extended_attributes

    Checks for the presence of extended_attributes in a query
    If present, it builds the dynamic extended attributes relations and rewrites the query to include the extended_attributes relation

=cut

sub handle_query_extended_attributes {
    my ( $self, $args ) = @_;

    my $attributes      = $args->{attributes};
    my $filtered_params = $args->{filtered_params};

    if (   reftype( $attributes->{prefetch} )
        && reftype( $attributes->{prefetch} ) eq 'ARRAY'
        && grep ( /extended_attributes/, @{ $attributes->{prefetch} } ) )
    {

        my @array = $self->_get_extended_attributes_entries($filtered_params);

        # Calling our private method to build the extended attributes relations
        my @joins = $self->_build_extended_attributes_relations( \@array );
        push @{ $attributes->{join} }, @joins;

    }
}

=head3 _get_extended_attributes_entries

    $self->_get_extended_attributes_entries( $filtered_params, 0 )

Recursive function that returns the rewritten extended_attributes query entries.

Given:
[
    '-and',
    [
        {
            'extended_attributes.code'      => 'CODE_1',
            'extended_attributes.attribute' => { 'like' => '%Bar%' }
        },
        {
            'extended_attributes.attribute' => { 'like' => '%Bar%' },
            'extended_attributes.code'      => 'CODE_2'
        }
    ]
];

Returns :

[
    'CODE_1',
    'CODE_2'
]

=cut

sub _get_extended_attributes_entries {
    my ( $self, $params, @array ) = @_;

    if ( reftype($params) && reftype($params) eq 'HASH' ) {

        # rewrite additional_field_values table query params
        @array = _rewrite_related_metadata_query( $params, 'field_id', 'value', @array )
            if $params->{'extended_attributes.field_id'};

        # rewrite borrower_attributes table query params
        @array = _rewrite_related_metadata_query( $params, 'code', 'attribute', @array )
            if $params->{'extended_attributes.code'};

        # rewrite illrequestattributes table query params
        @array = _rewrite_related_metadata_query( $params, 'type', 'value', @array )
            if $params->{'extended_attributes.type'};

        foreach my $key ( keys %{$params} ) {
            return $self->_get_extended_attributes_entries( $params->{$key}, @array );
        }
    } elsif ( reftype($params) && reftype($params) eq 'ARRAY' ) {
        foreach my $ea_instance (@$params) {
            @array = $self->_get_extended_attributes_entries( $ea_instance, @array );
        }
        return @array;
    } else {
        return @array;
    }
}

=head3 _rewrite_related_metadata_query

        $extended_attributes_entries =
            _rewrite_related_metadata_query( $params, 'field_id', 'value', @array )

Helper function that rewrites all subsequent extended_attributes queries to match the alias generated by the dbic self left join
Take the below example (patrons):
        [
            {
                "extended_attributes.attribute":{"like":"%123%"},
                "extended_attributes.code":"CODE_1"
            }
        ],
        [
            {
                "extended_attributes.attribute":{"like":"%abc%" },
                "extended_attributes.code":"CODE_2"
            }
        ]

It'll be rewritten as:
        [
            {
                'extended_attributes_CODE_1.attribute' => { 'like' => '%123%' },
                'extended_attributes_CODE_1.code' => 'CODE_1'
            }
        ],
            [
            {
                'extended_attributes_CODE_2.attribute' => { 'like' => '%abc%' },
                'extended_attributes_CODE_2.code' => 'CODE_2'
            }
        ]

=cut

sub _rewrite_related_metadata_query {
    my ( $params, $key, $value, @array ) = @_;

    if ( ref \$params->{ 'extended_attributes.' . $key } eq 'SCALAR' ) {
        my $old_key_value = delete $params->{ 'extended_attributes.' . $key };
        my $new_key_value = "extended_attributes_$old_key_value" . "." . $key;
        $params->{$new_key_value} = $old_key_value;

        my $old_value_value = delete $params->{ 'extended_attributes.' . $value };
        my $new_value_value = "extended_attributes_$old_key_value" . "." . $value;
        $params->{$new_value_value} = $old_value_value;
        push @array, $old_key_value;
    }

    return @array;
}

=head3 _build_extended_attributes_relations

    Method to dynamically add has_many relations for Koha classes that support extended_attributes.

    Returns a list of relation accessor names.

=cut

sub _build_extended_attributes_relations {
    my ( $self, $types ) = @_;

    return if ( !grep ( /extended_attributes/, keys %{ $self->_resultset->result_source->_relationships } ) );

    my $ea_config = $self->extended_attributes_config;

    my $result_source = $self->_resultset->result_source;
    for my $type ( @{$types} ) {
        $result_source->add_relationship(
            "extended_attributes_$type",
            "$ea_config->{schema_class}",
            sub {
                my $args = shift;

                return {
                    "$args->{foreign_alias}.$ea_config->{id_field}->{foreign}" =>
                        { -ident => "$args->{self_alias}.$ea_config->{id_field}->{self}" },
                    "$args->{foreign_alias}.$ea_config->{key_field}" => { '=', $type },
                };
            },
            {
                accessor       => 'multi',
                join_type      => 'LEFT',
                cascade_copy   => 0,
                cascade_delete => 0,
                is_depends_on  => 0
            },
        );

    }
    return map { 'extended_attributes_' . $_ } @{$types};
}

1;
