package Local::Class::DBI::Relationship::MightHave;

use strict;
use warnings;

use base 'Local::Class::DBI::Relationship';

sub remap_arguments {
	my ($proto, $class, $method, $f_class, @methods) = @_;
	$class->_require_class($f_class);

	my $args = { };
	if (@methods) {
		if (ref($methods[-1]) eq 'HASH') {
			$args = pop @methods;
		}
	}

	$args->{'import'} = \@methods;

	return ($class, $method, $f_class, $args);
}

sub triggers {
	my $self = shift;

	my $method = $self->accessor;

	return (
		before_update => sub {
			return unless (@{ $self->args->{import} });
			my ($obj) = @_;
			if (defined $obj->{"_${method}_object"} && (my $for_obj = $obj->$method())) { $for_obj->update }
		},

		before_delete => sub {
			return if ($self->args->{'cascade'} eq 'none');
			if (my $for_obj = shift->$method()) { $for_obj->delete }
		},
	);
}

sub methods {
	my $self = shift;
	my ($class, $method) = ($self->class, $self->accessor);
	return (
		$method => $self->_object_accessor,
		map { $_ => $self->_imported_accessor($_) } @{ $self->args->{import} }
	);
}

sub _object_accessor {
	my $rel = shift;
	my ($class, $method) = ($rel->class, $rel->accessor);
	my $join_key = "__joined__${method}";
	return sub {
		my $self = shift;
		my $meta = $class->meta_info($rel->name => $method);
		my ($f_class, @extra) =
			($meta->foreign_class, @{ $meta->args->{import} });
		return
			if defined($self->{"_${method}_object"})
			&& $self->{"_${method}_object"}
			->isa('Local::Class::DBI::Object::Has::Been::Deleted');
		unless (defined $self->{"_${method}_object"}) {
			my ($primary_column) = $f_class->primary_columns;
			if (my $data = delete $self->{$join_key}) {
				$self->{"_${method}_object"} = $f_class->construct($data);
			} elsif (exists $rel->args->{foreign_key}) {
				($self->{"_${method}_object"}) = $f_class->search($rel->args->{foreign_key} => $self->id);
			} else {
				$self->{"_${method}_object"} = $f_class->retrieve($self->id);
			}
		}
		return $self->{"_${method}_object"};
	};
}

sub _imported_accessor {
	my ($rel, $name) = @_;
	my ($class, $method) = ($rel->class, $rel->accessor);
	return sub {
		my $self = shift;
		my $meta = $class->meta_info($rel->name => $method);
		my ($f_class, @extra) =
			($meta->foreign_class, @{ $meta->args->{import} });
		my $for_obj = $self->$method() || do {
			return unless @_;    # just fetching
			my $val = shift;
			$f_class->insert(
				{ $f_class->primary_column => $self->id, $name => $val });
			$self->$method();
		};
		$for_obj->$name(@_);
	};
}

1;
