use strict;
use warnings;
use Test::More;
use Test::Deep;
use Test::Exception;
use FindBin qw( $Bin );
use Path::Tiny qw( path );
my $SHARE_DIR = path( $Bin, 'share' );

use Beam::Wire;
sub invalid ($$$$);

subtest 'config file does not exist' => sub {
    throws_ok { Beam::Wire->new( file => 'DOES_NOT_EXIST.yml' ) } 'Beam::Wire::Exception::Constructor';
    like $@, qr{\QContainer file 'DOES_NOT_EXIST.yml' does not exist}, 'stringifies';
};

subtest 'config file cannot be read' => sub {
    my $file = $SHARE_DIR->child( config => 'bad_char.yml' );
    throws_ok { Beam::Wire->new( file => $file ) }
        'Beam::Wire::Exception::Config';
    like $@, qr{Could not load container file "\Q$file\E": Error from config parser: .+};
};

subtest "get a service that doesn't exist" => sub {
    my $wire = Beam::Wire->new;
    throws_ok { $wire->get( 'foo' ) } 'Beam::Wire::Exception::NotFound';
    is $@->name, 'foo';
    like $@, qr{\QService 'foo' not found}, 'stringifies';

    subtest 'not found with file shows file name' => sub {
        my $path = $SHARE_DIR->child( 'file.yml' )->stringify;
        my $wire = Beam::Wire->new( file => $path );
        throws_ok { $wire->get( 'does_not_exist' ) } 'Beam::Wire::Exception::NotFound';
        is $@->name, 'does_not_exist';
        like $@, qr{\QService 'does_not_exist' not found in file '$path'}, 'stringifies';
    };
};

subtest "extend a service that doesn't exist" => sub {
    my $wire = Beam::Wire->new(
        config => {
            foo => {
                extends => 'bar',
            },
        },
    );
    throws_ok { $wire->get( 'foo' ) } 'Beam::Wire::Exception::NotFound';
    is $@->name, 'bar';
    like $@, qr{\QService 'bar' not found}, 'stringifies';
    invalid $wire, 'bar', 'NotFound', qr{\QService 'bar' not found};
};

subtest "service with both value and class/extends" => sub {
    subtest "class + value" => sub {
        my $wire;
        lives_ok {
            $wire = Beam::Wire->new(
                config => {
                    foo => {
                        class => 'My::ArgsTest',
                        value => 'foo',
                    }
                }
            );
        };
        throws_ok { $wire->get( 'foo' ) } 'Beam::Wire::Exception::InvalidConfig';
        is $@->name, 'foo';
        like $@, qr{\QInvalid config for service 'foo': "value" cannot be used with "class" or "extends"}, 'stringifies';
        invalid $wire, 'foo', 'InvalidConfig', qr{\QInvalid config for service 'foo': "value" cannot be used with "class" or "extends"};
    };
    subtest "extends + value" => sub {
        my $wire;
        lives_ok {
            $wire = Beam::Wire->new(
                config => {
                    bar => {
                        value => 'bar',
                    },
                    foo => {
                        extends => 'bar',
                        value => 'foo',
                    }
                }
            );
        };
        throws_ok { $wire->get( 'foo' ) } 'Beam::Wire::Exception::InvalidConfig';
        is $@->name, 'foo';
        like $@, qr{\QInvalid config for service 'foo': "value" cannot be used with "class" or "extends"}, 'stringifies';
        invalid $wire, 'foo', 'InvalidConfig', qr{\QInvalid config for service 'foo': "value" cannot be used with "class" or "extends"};
    };
    subtest "value in extended service" => sub {
        my $wire;
        lives_ok {
            $wire = Beam::Wire->new(
                config => {
                    bar => {
                        value => 'bar',
                    },
                    foo => {
                        extends => 'bar',
                        class => 'foo',
                    }
                }
            );
        };
        throws_ok { $wire->get( 'foo' ) } 'Beam::Wire::Exception::InvalidConfig';
        is $@->name, 'foo';
        like $@, qr{\QInvalid config for service 'foo': "value" cannot be used with "class" or "extends"}, 'stringifies';
        invalid $wire, 'foo', 'InvalidConfig', qr{\QInvalid config for service 'foo': "value" cannot be used with "class" or "extends"};
    };

    subtest "exception shows file name" => sub {
        my $path = $SHARE_DIR->child( 'file.yml' )->stringify;
        my $wire;
        lives_ok {
            $wire = Beam::Wire->new(
                file => $path,
                config => {
                    bar => {
                        value => 'bar',
                    },
                    foo => {
                        extends => 'bar',
                        class => 'foo',
                    }
                }
            );
        };
        throws_ok { $wire->get( 'foo' ) } 'Beam::Wire::Exception::InvalidConfig';
        is $@->name, 'foo';
        like $@, qr{\QInvalid config for service 'foo': "value" cannot be used with "class" or "extends" in file '$path'}, 'stringifies';
    };
};

sub invalid ($$$$) {
    my ( $wire, $name, $exception, $error_regex ) = @_;
    local $Test::Builder::Level = $Test::Builder::Level + 1;
    throws_ok { $wire->validate() } "Beam::Wire::Exception::$exception";
    is $@->name, $name;
    like $@, $error_regex;
}

done_testing;