use strict;
use warnings;
use 5.022;

package Alien::Build::Wizard 0.06 {

  use Moose;
  use Moose::Util::TypeConstraints;
  use MooseX::StrictConstructor;
  use experimental qw( signatures postderef );
  use Data::Section::Simple qw( get_data_section );
  use Alien::Build::Wizard::Detect;
  use Alien::Build::Wizard::Questions qw( :all );
  use namespace::autoclean;

  # ABSTRACT: Alien distribution creation wizard

  has detect => (
    is       => 'ro',
    isa      => 'Alien::Build::Wizard::Detect',
    lazy     => 1,
    default => sub ($self) {
      for(1..20)
      {
        my $url = $self->chrome->ask(QUESTION_URL);

        if($url eq '')
        {
          $self->chrome->say("URL is required");
          next;
        }

        my $detect = eval { Alien::Build::Wizard::Detect->new( uri => $url ) };
        if(my $error = $@)
        {
          $self->chrome->say("there appears to have been a problem fetching or detecting that tarball.");
          $self->chrome->say("$error");
        }
        else
        {
          return $detect;
        }
      }
      die "Bailing unable to get good input";
    },
  );

  has chrome => (
    is      => 'ro',
    isa     => 'Alien::Build::Wizard::Chrome',
    lazy    => 1,
    default => sub ($self) {
      require Alien::Build::Wizard::Chrome;
      Alien::Build::Wizard::Chrome->new;
    },
  );

  has class_name => (
    is      => 'ro',
    isa     => 'Str',
    lazy    => 1,
    default => sub ($self) {
      $self->chrome->ask(QUESTION_CLASS_NAME, 'Alien::' . $self->detect->name);
    },
  );

  has start_url => (
    is      => 'ro',
    isa     => 'URI',
    lazy    => 1,
    default => sub ($self) {
      if($self->latest)
      {
        my $url = $self->detect->uri->clone;
        my $path = $url->path;
        $path =~ s{/[^/]*?$}{};
        $url->path($path);
        return $url;
      }
      else
      {
        return $self->detect->uri;
      }
    },
  );

  has human_name => (
    is      => 'ro',
    isa     => 'Str',
    lazy    => 1,
    default => sub ($self) {
      $self->chrome->ask(QUESTION_HUMAN_NAME, $self->detect->name);
    },
  );

  has pkg_names => (
    is      => 'ro',
    isa     => 'ArrayRef[Str]',
    lazy    => 1,
    default => sub ($self) {
      [split /\s+/, $self->chrome->ask(QUESTION_PKG_NAMES, join ' ', $self->detect->pkg_config->@*)];
    },
  );

  has extract_format => (
    is      => 'ro',
    isa     => 'Str',
    lazy    => 1,
    default => sub ($self) {
      my $basename = Path::Tiny->new($self->detect->uri->path)->basename;

      # tar format is usually .tar or .tar.gz .tar.bz2 etc.
      if($basename =~ /\.(tar(\..*)?)$/)
      {
        return $1;
      }
      # non-greedy to only get the last . for non tars
      elsif($basename =~ /\.(.*?)$/)
      {
        return $1;
      }
      # fallback on .fixme, user will probably have to update
      else
      {
        return 'fixme';
      }
    },
  );

  has build_type => (
    is      => 'ro',
    isa     => 'Str',
    lazy    => 1,
    default => sub ($self) {
      my @types = $self->detect->build_type->@*;
      if(@types == 0)
      {
        $self->chrome->say("Unable to detect build system used by the package.  You can select manual to specify the build commands directly, or select one of the standard build systems");
      }
      elsif(@types == 1)
      {
        $self->chrome->say("The build system was detected as $types[0]; that is probably correct");
      }
      else
      {
        $self->chrome->say("Multiple build systems were detected in the tarball; select the most reliable one of: @types");
      }
      my $default = $types[0];
      scalar $self->chrome->choose(QUESTION_BUILD_SYSTEM, ['manual','autoconf','cmake','make'], \@types);
    },
  );

  has alien_types => (
    is      => 'ro',
    isa     => 'HashRef[Int]',
    lazy    => 1,
    default => sub ($self) {
      while(1)
      {
        my @types = $self->chrome->choose(QUESTION_ALIEN_TYPE, ['tool','xs','ffi'], ['xs']);
        unless(@types)
        {
          $self->chrome->say("You must select at least one type");
          next;
        }
        my %answer = map { $_ => 1 } @types;
        return \%answer;
      }
    }
  );

  has latest => (
    is      => 'ro',
    isa     => 'Int',
    lazy    => 1,
    default => sub ($self) {
      my $value = $self->chrome->choose(QUESTION_LATEST, ['latest','specific'], ['latest']);
      $value eq 'latest' ? 1 : 0;
    },
  );

  has prefix => (
    is      => 'ro',
    isa     => 'Str',
    lazy    => 1,
    default => sub ($self) {
      my $path = $self->detect->uri->path;
      my($file) = $path =~ /\/([^\/]*?)$/;
      my($prefix) = $file =~ /^([^-^\.]+)/;
      $prefix;
    }
  );

  sub generate_content ($self)
  {
    my %files;

    require Template;
    my $tt = Template->new(
      FILTERS => {
        regex_escape => sub ($text) { $text =~ s/\./\\./gr },
      },
    );

    my $pod = sub ($name) { "=$name" };

    {
      my $pm = 'lib/' . $self->class_name . ".pm";
      $pm =~ s{::}{/};
      my $template = get_data_section 'Module.pm';
      $template =~ s/\s+$/\n/;
      die "no template Module.pm" unless $template;
      $tt->process(\$template, { wizard => $self, pod => $pod }, \($files{$pm} = '')) or die $tt->error;
    }

    foreach my $path (qw( alienfile t/basic.t ))
    {
      my $template = get_data_section $path;
      $template =~ s/\s+$/\n/;
      die "no template $path" unless $template;
      my $store_path = $path;
      $store_path = "t/@{[ lc($self->class_name =~ s/::/_/gr) ]}.t" if $path eq 't/basic.t';
      $tt->process(\$template, { wizard => $self, pod => $pod }, \($files{$store_path} = '')) or die $tt->error;
    }

    \%files;
  }

}

package Alien::Build::Wizard;

1;

=pod

=encoding UTF-8

=head1 NAME

Alien::Build::Wizard - Alien distribution creation wizard

=head1 VERSION

version 0.06

=head1 SYNOPSIS

 % perldoc Dist::Zilla::MintingProfile::AlienBuild

=head1 DESCRIPTION

This class is private.

=head1 SEE ALSO

=over 4

=item L<Dist::Zilla::MintingProfile::AlienBuild>

=back

=head1 AUTHOR

Graham Ollis <plicease@cpan.org>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2021 by Graham Ollis.

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

=cut

__DATA__

@@ Module.pm
package [% wizard.class_name %];

use strict;
use warnings;
use base qw( Alien::Base );
use 5.008004;

1;

[% pod('head1') %] NAME

[% wizard.class_name %] - Find or build [% wizard.human_name %]

[% pod('head1') %] SYNOPSIS
[%- IF wizard.alien_types.xs %]

From L<ExtUtils::MakeMaker>:

 use ExtUtils::MakeMaker;
 use Alien::Base::Wrapper ();

 WriteMakefile(
   Alien::Base::Wrapper->new('[% wizard.class_name %]')->mm_args2(
     NAME => 'FOO::XS',
     ...
   ),
 );

From L<Module::Build>:

 use Module::Build;
 use Alien::Base::Wrapper qw( [% wizard.class_name %] !export );
 use [% wizard.class_name %];

 my $build = Module::Build->new(
   ...
   configure_requires => {
     'Alien::Base::Wrapper' => '0',
     '[% wizard.class_name %]' => '0',
     ...
   },
   Alien::Base::Wrapper->mb_args,
   ...
 );

 $build->create_build_script;

From L<Inline::C> / L<Inline::CPP> script:

 use Inline 0.56 with => '[% wizard.class_name %]';

From L<Dist::Zilla>

 [@Filter]
 -bundle = @Basic
 -remove = MakeMaker

 [Prereqs / ConfigureRequires]
 [% wizard.class_name %] = 0

 [MakeMaker::Awesome]
 header = use Alien::Base::Wrapper qw( [% wizard.class_name %] !export );
 WriteMakefile_arg = Alien::Base::Wrapper->mm_args
[%- END %]
[%- IF wizard.alien_types.ffi %]

From L<FFI::Platypus>:

 use FFI::Platypus;
 use [% wizard.class_name %];

 my $ffi = FFI::Platypus->new(
   lib => [ [% wizard.class_name %]->dynamic_libs ],
 );
[%- END %]
[%- IF wizard.alien_types.tool %]

Command line tool:

 use [% wizard.class_name %];
 use Env qw( @PATH );

 unshift @PATH, [% wizard.class_name %]->bin_dir;
[%- END %]

[% pod('head1') %] DESCRIPTION

This distribution provides [% wizard.human_name %] so that it can be used by other
Perl distributions that are on CPAN.  It does this by first trying to
detect an existing install of [% wizard.human_name %] on your system.  If found it
will use that.  If it cannot be found, the source code will be downloaded
from the internet and it will be installed in a private share location
for the use of other modules.

[% pod('head1') %] SEE ALSO

[% pod('over') %] 4

[% pod('item') %] L<Alien>

Documentation on the Alien concept itself.

[% pod('item') %] L<Alien::Base>

The base class for this Alien.

[% pod('item') %] L<Alien::Build::Manual::AlienUser>

Detailed manual for users of Alien classes.

[% pod('back') %]

[% pod('cut') %]

@@ alienfile
use alienfile;

[% IF wizard.pkg_names.size > 0 -%]
plugin PkgConfig => [% IF wizard.pkg_names.size > 1 %][[% FOREACH name IN wizard.pkg_names %]'[% name %]'[% UNLESS loop.last %], [% END %][% END %]][% ELSE %]'[% wizard.pkg_names.0 %]'[% END %];
[% ELSE -%]
# replace this with your own system probe.
# See Alien::Build::Plugin::Probe and
# Alien::Build::Plugin::PkgConfig for common
# probe plugins.
probe sub { 'share' };
[% END -%]

share {
  start_url '[% wizard.start_url %]';
[% IF wizard.latest -%]
  plugin Download => (
    filter  => qr/^[% wizard.prefix %].*[% wizard.extract_format | regex_escape %]$/,
    version => qr/([0-9\.]+)/,
  );
[% ELSE -%]
  plugin Download => ();
[% END -%]
[% IF wizard.extract_format == 'fixme' -%]

  # archive format was not detected, see
  # https://metacpan.org/pod/Alien::Build::Plugin::Extract::Negotiate
  # for valid formats.
[% END -%]
  plugin Extract => '[% wizard.extract_format %]';
[% IF wizard.built_type == 'manual' -%]
  build [
    # TODO
    # See https://metacpan.org/pod/alienfile#build
  ];
[% ELSIF wizard.build_type == 'autoconf' -%]
  plugin 'Build::Autoconf';
[% IF wizard.alien_types.tool AND wizard.alien_types.ffi -%]
  build [
    '%{configure} --enable-static --disable-shared',
    '%{make}',
    '%{make} install',
  ];
  ffi {
    build [
      '%{configure} --disable-static --enable-shared',
      '%{make}',
      '%{make} install',
    ];
  };
[% ELSE -%]
  build [
    '%{configure} --[% IF wizard.alien_types.xs OR wizard.alien_types.tool %]enable[% ELSE %]disable[% END %]-static --[% IF wizard.alien_types.ffi %]enable[% ELSE %]disable[% END %]-shared',
    '%{make}',
    '%{make} install',
  ];
[% END -%]
[% ELSIF wizard.build_type == 'cmake' -%]
  plugin 'Build::CMake';
  build [
    ['%{cmake}', @{ meta->prop->{plugin_build_cmake}->{args} }, '%{.install.extract}'],
    '%{make}',
    '%{make} install',
  ];
[% ELSIF wizard.build_type == 'make' -%]
  build [
    # NOTE: you will probably need to set a PREFIX and possibly DISTDIR
    # https://metacpan.org/pod/Alien::Build#prefix1
    # https://metacpan.org/pod/Alien::Build#destdir
    '%{make}',
    '%{make} install',
  ];
[% END -%]
  plugin 'Gather::IsolateDynamic';
}

@@ t/basic.t
use Test2::V0;
use Test::Alien;
use Test::Alien::Diag;
use [% wizard.class_name %];

alien_diag '[% wizard.class_name %]';
alien_ok '[% wizard.class_name %]';

[%- IF wizard.alien_types.tool %]
# run_ok([ ... ])
#   ->success
#   ->out_like(qr/ ... /);

[%- END %]
[%- IF wizard.alien_types.xs %]
# my $xs = <<'END';
# #include "EXTERN.h"
# #include "perl.h"
# #include "XSUB.h"
# ...
#
# MODULE = main PACKAGE = main
#
# ...
# END
# xs_ok $xs, with_subtest {
#   ...
# };

[%- END %]
[%- IF wizard.alien_types.ffi %]
# ffi_ok { symbols => [...] }, with_subtest {
#   my $ffi = shift;
#   ...
# };

[%- END %]
done_testing;