package JSONAPI::Document::Builder::Relationships;
$JSONAPI::Document::Builder::Relationships::VERSION = '2.4';
=head1 NAME

JSONAPI::Document::Builder::Relationships - Related Resource Document builder

=head1 VERSION

version 2.4

=head1 DESCRIPTION

Builds the related resource document for a given row.

=cut

use Moo;
with 'JSONAPI::Document::Builder::Role::Parameters',
    'JSONAPI::Document::Builder::Role::Attributes',
    'JSONAPI::Document::Builder::Role::Type';

use Carp ();

=head2 row

The C<DBIx::Class::Row> for C<relationship>.

Note this is not the relationship row, rather
it is its parent.

=cut

has row => (
    is       => 'ro',
    required => 1,
);

=head2 relationship

String name of the relationship.

=cut

has relationship => (
    is       => 'ro',
    required => 1,
);

=head2 with_attributes

Boolean; Default: false

If specified, will build the relationship with attributes
instead of links.

Default behaviour is to build with links.

=cut

has with_attributes => (
    is      => 'ro',
    default => sub { 0 },
);

=head2 build : HashRef

Main caller method; Builds the related resource document.

=cut

sub build {
    my ($self) = @_;
    my $row    = $self->row;
    my $rel    = $self->relationship;

    unless ($row->has_relationship($rel)) {
        return undef;
    }

    if ($self->with_attributes) {
        return $self->build_document($row, $rel);
    }

    return $self->build_links_document($row, $rel);
}

=head2 build_links_document(DBIx::Class::Row $row, Str $relationship) : HashRef

Builds a HashRef containing strings that represent URLs for fetching
the given relationship, as well as the relationship ID(s).

For referential purposes, B<self> and B<related> mean the following:

=over

=item self

A link pointing to the relationship itself regardless of whether it is
a one-to-one or has-many type of relationship. It contains the word
"relationship" in the URL.

The specification defines this link as the B<link to the relationship itself>
in the context of the primary resource. This means that the resource(s)
returned from this URL should be directly related to the primary resource,
i.e. C<$dbic_row-E<gt>$relationship>.

=item related

Behaves the same as "self" except that its URL structure is
different. I fail to see the difference.

=back

=cut

sub build_links_document {
    my ($self, $row, $relationship) = @_;

    unless ($self->api_url) {
        Carp::confess('Missing required argument: api_url');
    }

    my $relationship_type = $self->document_type($relationship);

    my $data;
    my $rel_info = $row->result_source->relationship_info($relationship);
    if ($rel_info->{attrs}->{accessor} eq 'multi') {
        $data = [];
        my @rs = $row->$relationship->all();
        foreach my $related_row (@rs) {
            push @$data, { id => $related_row->id, type => $relationship_type };
        }
    } else {
        if (my $related_row = $row->$relationship) {
            $data = {
                id   => $related_row->id,
                type => $relationship_type
            };
        }
    }

    my $row_type = $self->document_type($row->result_source->source_name());

    return {
        links => {
            self => $self->api_url . '/'
                . $row_type . '/'
                . $row->id
                . '/relationships/'
                . $self->format_type($relationship),
            related => $self->api_url . '/' . $row_type . '/' . $row->id . '/' . $self->format_type($relationship),
        },
        data => $data,
    };
}

=head2 build_document(DBIx::Class::Row $row, Str $relationship) : HashRef

Builds a HashRef of the relationship(s) with attributes.

=cut

sub build_document {
    my ($self, $row, $relationship) = @_;
    my $rel_info = $row->result_source->relationship_info($relationship);
    if ($rel_info->{attrs}->{accessor} eq 'multi') {
        my @results;
        my @rs = $row->$relationship->all();
        foreach my $related_row (@rs) {
            push @results, $self->build_single_document($related_row, $relationship);
        }
        return { data => \@results };
    } else {
        if (my $related_row = $row->$relationship) {
            return { data => $self->build_single_document($related_row, $relationship), };
        } else {
            return { data => undef };
        }
    }
}

=head2 build_single_document(DBIx::Class::Row $related_row, Str $relationship) : HashRef

Builds a HashRef representing a single relationship row.

=cut

sub build_single_document {
    my ($self, $related_row, $relationship) = @_;
    return {
        id         => $related_row->id,
        type       => $self->document_type($relationship),
        attributes => $self->get_attributes($related_row),
    };
}

1;