package Zonemaster::Backend::Error;
use Moose;
use Data::Dumper;

use overload '""' => \&as_string;

has 'message' => (
    is => 'ro',
    isa => 'Str',
    required => 1,
);

has 'code' => (
    is => 'ro',
    isa => 'Int',
    required => 1,
);

has 'data' => (
    is => 'ro',
    isa => 'Any',
    default => undef,
);

sub as_hash {
    my $self = shift;
    my $error = {
        code => $self->code,
        message => $self->message,
        error => ref($self),
    };
    $error->{data} = $self->data if defined $self->data;
    return $error;
}

sub as_string {
    my $self = shift;
    my $str = sprintf "%s (code %d).", $self->message, $self->code;
    if (defined $self->data) {
        $str .= sprintf " Context: %s", $self->_data_dump;
    }
    return $str;
}

sub _data_dump {
    my $self = shift;
    local $Data::Dumper::Indent = 0;
    local $Data::Dumper::Terse = 1;
    my $data = Dumper($self->data);
    $data =~ s/[\n\r]/ /g;
    return $data ;
}

package Zonemaster::Backend::Error::Internal;
use Moose;

use overload '""' => \&as_string;

extends 'Zonemaster::Backend::Error';

has '+message' => (
    default => 'Internal server error'
);

has '+code' => (
    default => -32603
);

has 'reason' => (
    isa => 'Str',
    is => 'ro'
);

has 'method' => (
    is => 'ro',
    isa => 'Str',
    builder => '_build_method'
);

sub _build_method {
    my $s = 0;
    while (my @c = caller($s)) {
        $s ++;
        last if $c[3] eq 'Moose::Object::new';
    }
    my @c = caller($s);
    if ($c[3] =~ /^(.*)::handle_exception$/ ) {
        @c = caller(++$s);
    }

    return $c[3];
}

sub as_string {
    my $self = shift;

    my $reason = $self->reason;
    $reason =~ s/\s+/ /g;
    $reason =~ s/^\s+|\s+$//g;

    my $str = sprintf "Caught %s in the `%s` method: %s", ref($self), $self->method, $reason;
    if (defined $self->data) {
        $str .= sprintf " Context: %s", $self->_data_dump;
    }
    return $str;
}

around as_hash => sub {
    my ($orig, $self) = @_;

    my $hash = $self->$orig;
    $hash->{reason} = $self->reason;
    $hash->{method} = $self->method;
    return $hash;
};


package Zonemaster::Backend::Error::ResourceNotFound;
use Moose;

extends 'Zonemaster::Backend::Error';

has '+message' => (
    default => 'Resource not found'
);

has '+code' => (
    default => -32000
);

package Zonemaster::Backend::Error::PermissionDenied;
use Moose;

extends 'Zonemaster::Backend::Error';

has '+message' => (
    default => 'Permission denied'
);

has '+code' => (
    default => -32001
);

package Zonemaster::Backend::Error::Conflict;
use Moose;

extends 'Zonemaster::Backend::Error';

has '+message' => (
    default => 'Conflicting resource'
);

has '+code' => (
    default => -32002
);

package Zonemaster::Backend::Error::JsonError;
use Moose;

extends 'Zonemaster::Backend::Error::Internal';

1;
