use 5.010001;
use strict;
use warnings;

package Dist::Inktly::Minty;

our $AUTHORITY = 'cpan:TOBYINK';
our $VERSION   = '0.005';

use Carp;
use File::chdir;
use Path::Tiny 'path';
use Software::License;
use Text::Template;
use URI::Escape qw[];

{
	my %templates;
	my $key = undef;
	while (my $line = <DATA>)
	{
		if ($line =~ /^COMMENCE\s+(.+)\s*$/)
		{
			$key = $1;
		}
		elsif (defined $key)
		{
			$templates{$key} .= $line;
		}
	}
	sub _has_template
	{
		my $class = shift;
		my ($key) = @_;
		exists $templates{$key};
	}
	sub _get_template
	{
		my $class = shift;
		my ($key) = @_;
		'Text::Template'->new(-type=>'string', -source=>$templates{$key});
	}
	sub _get_template_names
	{
		my $class = shift;
		return keys %templates;
	}
}

sub _fill_in_template
{
	my $self = shift;
	my ($template) = @_;
	$template = $self->_get_template($template) unless ref $template;
	
	my %hash = ();
	while (my ($k, $v) = each %$self)
	{
		$hash{$k} = ref $v ? \$v : $v;
	}
	
	$template->fill_in(-hash => \%hash);
}

sub _file
{
	my $self = shift;
	my $file = path(
		$self->{destination},
		sprintf('p5-%s', lc($self->{dist_name}))
	)->child($_[0]);
	$file->parent->mkpath;
	return $file;
}

sub create
{
	my $class = shift;
	my ($name, %options) = @_;
	
	$options{module_name} = $name;
	$options{dist_name}   = $name;
	
	if ($name =~ /::/)
	{
		$options{dist_name} =~ s/::/-/g;
	}
	elsif ($name =~ /\-/)
	{
		$options{module_name} =~ s/\-/::/g;
	}
	
	my $self = bless \%options, $class;
	$self->set_defaults;
	$self->create_module;
	$self->create_dist_ini;
	$self->create_metadata;
	$self->create_tests;
	$self->create_author_tests;
	$self->run_then;
	return $self;
}

sub set_defaults
{
	my $self = shift;
	
	croak "Need an author name."   unless defined $self->{author}{name};
	croak "Need an author cpanid." unless defined $self->{author}{cpanid};
	
	$self->{author}{cpanid} = lc $self->{author}{cpanid};
	$self->{author}{mbox} ||= sprintf('%s@cpan.org', $self->{author}{cpanid});

	$self->{backpan} ||= sprintf('http://backpan.cpan.org/authors/id/%s/%s/%s/',
		substr(uc $self->{author}{cpanid}, 0, 1),
		substr(uc $self->{author}{cpanid}, 0, 2),
		uc $self->{author}{cpanid},
	);
	
	$self->{abstract} ||= 'a module that does something-or-other';
	$self->{version}  ||= '0.001';
	$self->{version_ident} = 'v_'.$self->{version};
	$self->{version_ident} =~ s/\./-/g;
	$self->{destination} ||= './';
	
	unless ($self->{module_filename})
	{
		$self->{module_filename} = 'lib::'.$self->{module_name};
		$self->{module_filename} =~ s/::/\//g;
		$self->{module_filename} .= '.pm';
	}
	
	unless ($self->{unit_test_filename})
	{
		$self->{unit_test_filename} = $self->{module_filename};
		$self->{unit_test_filename} =~ s/\.pm$/\.t/;
		$self->{unit_test_filename} =~ s/lib/t\/unit/;
	}
	
	$self->{copyright}{holder} ||= $self->{author}{name};
	$self->{copyright}{year}   ||= 1900 + [localtime]->[5];
	
	$self->{licence_class} ||= 'Software::License::Perl_5';
	eval sprintf('use %s;', $self->{licence_class});
	$self->{licence} = $self->{licence_class}->new({
		year    => $self->{copyright}{year},
		holder  => $self->{copyright}{holder},
	});
}

sub create_module
{
	my $self = shift;
	$self->_file( $self->{module_filename} )->spew( $self->_fill_in_template('module') );
	return;
}

sub create_dist_ini
{
	my $self = shift;
	$self->_file('dist.ini')->spew($self->_fill_in_template('dist.ini'));
	return;
}

sub create_metadata
{
	my $self = shift;
	$self->_file($_)->spew($self->_fill_in_template($_))
		for grep { m#^meta/# } $self->_get_template_names;
	return;
}

sub create_tests
{
	my $self = shift;
	$self->_file($_)->spew($self->_fill_in_template($_))
		for grep { m#^t/# } $self->_get_template_names;
	mkdir( $self->_file('t/unit') );
	mkdir( $self->_file('t/integration') );
	$self->_file( $self->{unit_test_filename} )->spew( $self->_fill_in_template('unit-test') );
	return;
}

sub create_author_tests
{
	my $self = shift;
	
	$self->_file($_)->spew($self->_fill_in_template($_))
		for grep { m#^xt/# } $self->_get_template_names;
	
	my $xtdir = path("~/perl5/xt");
	if ( $xtdir->exists ) {
		$self->_file("xt/" . $_->relative($xtdir))->spew(scalar $_->slurp)
			for grep { $_ =~ /\.t$/ } $xtdir->children;
	}
	
	return;
}

sub run_then {
	my $self = shift;
	ref $self->{then} or return;
	my $dir = path(
		$self->{destination},
		sprintf('p5-%s', lc($self->{dist_name})),
	);
	local $CWD = "$dir";
	system($_) for @{ $self->{then} };
}

1;

=head1 NAME

Dist::Inktly::Minty - create distributions that will use Dist::Inkt

=head1 SYNOPSIS

  distinkt-mint Local::Example::Useful

=head1 STATUS

Experimental.

=head1 DESCRIPTION

Sets up a new distribution in the style TOBYINK likes.

This package provides just one (class) method:

=over

=item C<< Dist::Inktly::Minty->create($distname, %options) >>

Create a distribution directory including all needed files.

=back

There are various methods that may be useful for people subclassing this
class to look at (and possibly override).

=over

=item C<< set_defaults >>

=item C<< create_module >>

=item C<< create_dist_ini >>

=item C<< create_metadata >>

=item C<< create_tests >>

=item C<< create_author_tests >>

=back

=head1 SEE ALSO

L<Dist::Inkt>.

=head1 AUTHOR

Toby Inkster E<lt>tobyink@cpan.orgE<gt>.

=head1 COPYRIGHT AND LICENCE

This software is copyright (c) 2013-2023 by Toby Inkster.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=head1 DISCLAIMER OF WARRANTIES

THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.

=cut

__DATA__

COMMENCE module
use 5.010001;
use strict;
use warnings;

package {$module_name};

our $AUTHORITY = 'cpan:{uc $author->{cpanid}}';
our $VERSION   = '{$version}';

1;

{}__END__

{}=pod

{}=encoding utf-8

{}=head1 NAME

{$module_name} - {$abstract}

{}=head1 SYNOPSIS

{}=head1 DESCRIPTION

{}=head1 BUGS

Please report any bugs to
L<https://github.com/{lc $author->{cpanid}}/p5-{lc URI::Escape::uri_escape($dist_name)}/issues>.

{}=head1 SEE ALSO

{}=head1 AUTHOR

{$author->{name}} E<lt>{$author->{mbox}}E<gt>.

{}=head1 COPYRIGHT AND LICENCE

{$licence->notice}

{}=head1 DISCLAIMER OF WARRANTIES

THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.

COMMENCE dist.ini
;;class='Dist::Inkt::Profile::TOBYINK'
;;name='{$dist_name}'

COMMENCE meta/changes.pret
# This file acts as the project's changelog.

`{$dist_name} {$version} cpan:{uc $author->{cpanid}}`
	issued  {sprintf('%04d-%02d-%02d', 1900+(localtime)[5], 1+(localtime)[4], (localtime)[3])};
	label   "Initial release".

COMMENCE meta/doap.pret
# This file contains general metadata about the project.

@prefix : <http://usefulinc.com/ns/doap#>.

`{$dist_name}`
	:programming-language "Perl" ;
	:shortdesc            "{$abstract}";
	:homepage             <https://metacpan.org/release/{URI::Escape::uri_escape($dist_name)}>;
	:download-page        <https://metacpan.org/release/{URI::Escape::uri_escape($dist_name)}>;
	:bug-database         <https://github.com/{lc $author->{cpanid}}/p5-{lc URI::Escape::uri_escape($dist_name)}/issues>;
	:repository           [ a :GitRepository; :browse <https://github.com/{lc $author->{cpanid}}/p5-{lc URI::Escape::uri_escape($dist_name)}> ];
	:created              {sprintf('%04d-%02d-%02d', 1900+(localtime)[5], 1+(localtime)[4], (localtime)[3])};
	:license              <{$licence->url}>;
	:maintainer           cpan:{uc $author->{cpanid}};
	:developer            cpan:{uc $author->{cpanid}}.

<{$licence->url}>
	dc:title  "{$licence->name}".

COMMENCE meta/people.pret
# This file contains data about the project developers.

@prefix : <http://xmlns.com/foaf/0.1/>.

cpan:{uc $author->{cpanid}}
	:name  "{$author->{name}}";
	:page  <https://metacpan.org/author/{uc $author->{cpanid}}>;
	:mbox  <mailto:{$author->{mbox}}>.

COMMENCE meta/makefile.pret
# This file provides instructions for packaging.

@prefix : <http://ontologi.es/doap-deps#>.

`{$dist_name}`
	:runtime-requirement    [ :on "perl 5.010001"^^:CpanId ];
	:test-requirement       [ :on "Test2::V0"^^:CpanId ];
	:test-requirement       [ :on "Test2::Tools::Spec"^^:CpanId ];
	:test-requirement       [ :on "Test2::Require::AuthorTesting"^^:CpanId ];
	:test-requirement       [ :on "Test2::Require::Module"^^:CpanId ];
	:test-requirement       [ :on "Test2::Plugin::BailOnFail"^^:CpanId ];
	:develop-recommendation [ :on "Dist::Inkt 0.001"^^:CpanId ];
	.

COMMENCE t/01basic.t
{}=pod

{}=encoding utf-8

{}=head1 PURPOSE

Test that {$module_name} compiles.

{}=head1 AUTHOR

{$author->{name}} E<lt>{$author->{mbox}}E<gt>.

{}=head1 COPYRIGHT AND LICENCE

{$licence->notice}

{}=cut

use Test2::V0;
use Test2::Plugin::BailOnFail;

use {$module_name} ();

pass 'compiles ok';

done_testing;

COMMENCE t/00start.t
{}=pod

{}=encoding utf-8

{}=head1 PURPOSE

Print version numbers, etc.

{}=head1 AUTHOR

{$author->{name}} E<lt>{$author->{mbox}}E<gt>.

{}=head1 COPYRIGHT AND LICENCE

{$licence->notice}

{}=cut

use Test2::V0;

my @modules = qw(
	Carp
	Test2::V0
);

diag "\n####";
for my $mod ( sort @modules ) \{
	eval "require $mod;";
	diag sprintf( '%-20s %s', $mod, $mod->VERSION );
\}
diag "####";

pass;

done_testing;

COMMENCE unit-test
{}=pod

{}=encoding utf-8

{}=head1 PURPOSE

Unit tests for L<{$module_name}>.

{}=head1 AUTHOR

{$author->{name}} E<lt>{$author->{mbox}}E<gt>.

{}=head1 COPYRIGHT AND LICENCE

{$licence->notice}

{}=cut

use Test2::V0 -target => '{$module_name}';
use Test2::Tools::Spec;
use Data::Dumper;

describe "class `$CLASS`" => sub \{

	tests 'blah blah blah' => sub \{
	
		pass;
	\};
\};

done_testing;