#!/usr/bin/perl

use strict;
use warnings;

use Test::More;
use Test::Moose;

use Bread::Board;

{ # Abstract items ...
    package Desk;
    use Moose::Role;

    package Chair;
    use Moose::Role;

    package WorkArea;
    use Moose::Role;

    has 'desk' => (
        is       => 'ro',
        does     => 'Desk',
        required => 1,
    );

    has 'chair' => (
        is       => 'ro',
        does     => 'Chair',
        required => 1,
    );
}

# crappy stuff
{
    package CheapMetalDesk;
    use Moose;
    with 'Desk';

    package CheapOfficeChair;
    use Moose;
    with 'Chair';

    package Cubicle;
    use Moose;
    with 'WorkArea';
}

# good stuff
{
    package NiceWoodenDesk;
    use Moose;
    with 'Desk';

    package AeronChair;
    use Moose;
    with 'Chair';

    package Office;
    use Moose;
    with 'WorkArea';
}

{
    package Employee;
    use Moose;

    has [ 'first_name', 'last_name' ] => (
        is       => 'ro',
        isa      => 'Str',
        required => 1,
    );

    has 'work_area' => (
        is       => 'ro',
        does     => 'WorkArea',
        required => 1,
    );

    package Manager;
    use Moose;

    extends 'Employee';

    has '+work_area' => ( isa => 'Office' );
}

my $c = container 'Initech' => as {

    # Employees ...
    typemap 'Desk'     => infer( class => 'CheapMetalDesk' );
    typemap 'Chair'    => infer( class => 'CheapOfficeChair' );
    typemap 'WorkArea' => infer( class => 'Cubicle' );

    # Managers ...
    service 'managers_desk'  => (class => 'NiceWoodenDesk');
    service 'managers_chair' => (class => 'AeronChair');
    typemap 'Office'         => infer(
        dependencies => {
            desk  => depends_on('managers_desk'),
            chair => depends_on('managers_chair')
        }
    );

    typemap 'Employee' => infer;
    typemap 'Manager'  => infer;
};

my $micheal = $c->resolve(
    type       => 'Employee',
    parameters => {
        first_name => 'Micheal',
        last_name  => 'Bolton'
    }
);
my $samir = $c->resolve(
    type       => 'Employee',
    parameters => {
        first_name => 'Samir',
        last_name  => 'Nagheenanajar'
    }
);

isa_ok($micheal, 'Employee');
is($micheal->first_name, 'Micheal', '... got the right first name');
is($micheal->last_name, 'Bolton', '... got the right last name');

does_ok($micheal->work_area, 'WorkArea');
isa_ok($micheal->work_area, 'Cubicle');

does_ok($micheal->work_area->desk, 'Desk');
isa_ok($micheal->work_area->desk, 'CheapMetalDesk');

does_ok($micheal->work_area->chair, 'Chair');
isa_ok($micheal->work_area->chair, 'CheapOfficeChair');

isa_ok($samir, 'Employee');
is($samir->first_name, 'Samir', '... got the right first name');
is($samir->last_name, 'Nagheenanajar', '... got the right last name');

does_ok($samir->work_area, 'WorkArea');
isa_ok($samir->work_area, 'Cubicle');

does_ok($samir->work_area->desk, 'Desk');
isa_ok($samir->work_area->desk, 'CheapMetalDesk');

does_ok($samir->work_area->chair, 'Chair');
isa_ok($samir->work_area->chair, 'CheapOfficeChair');

isnt($micheal, $samir, '... two different employees');
isnt($micheal->work_area, $samir->work_area, '... two different cubicles');
isnt($micheal->work_area->chair, $samir->work_area->chair, '... two different cubicle chairs');
isnt($micheal->work_area->desk, $samir->work_area->desk, '... two different cubicle desks');

# managers

my $lundberg = $c->resolve(
    type       => 'Manager',
    parameters => {
        first_name => 'Bill',
        last_name  => 'Lundberg'
    }
);

isa_ok($lundberg, 'Manager');
is($lundberg->first_name, 'Bill', '... got the right first name');
is($lundberg->last_name, 'Lundberg', '... got the right last name');

does_ok($lundberg->work_area, 'WorkArea');
isa_ok($lundberg->work_area, 'Office');

does_ok($lundberg->work_area->desk, 'Desk');
isa_ok($lundberg->work_area->desk, 'NiceWoodenDesk');

does_ok($lundberg->work_area->chair, 'Chair');
isa_ok($lundberg->work_area->chair, 'AeronChair');

done_testing;
