#!/usr/bin/perl

use strict;
use Debian::PkgJs::Utils;
use Debian::PkgJs::Version;
use JSON;
use Pod::Usage;
use Pod::Text;

my $cmd = $0 =~ m#/pkgjs-(ln|pjson|main)$# ? $1 : shift(@ARGV);

pod2usage(
    -message => 'Missing command',
    -exitval => 1,
) unless $cmd;

pod2usage(
    -message => 'Bad command',
    -exitval => 2,
  )

  # $ regexp-assemble <<EOF
  # link_test_modules
  # link_external_modules
  # link_internal_modules
  # link_build_modules
  # clean_test_modules
  # clean_external_modules
  # clean_internal_modules
  # clean_build_modules
  # ln
  # components_list
  # modules_list
  # packages_list
  # root_components_list
  # root_modules_list
  # ordered_components_list
  # pjson
  # main
  # normalize_name
  # -h
  # -v
  # --help
  # --version
  # EOF
  unless $cmd =~
/^(?:c(?:lean_(?:(?:ex|in)ternal|build|test)_modules|omponents_list)|(?:root_(?:component|module)|ordered_component)s_list|l(?:ink_(?:(?:ex|in)ternal|build|test)_modules|n)|-(?:[hv]|-(?:version|help))|p(?:ackages_list|json)|m(?:odules_list|ain)|normalize_name)$/;

our $obj = bless {}, 'Debian::PkgJs::Utils';

use constant _sub => {
    '-v' => sub {
        return $VERSION;
    },
    '-h' => sub {
        return pod2usage( -verbose => 2 );
    },
    main => sub {
        return $obj->pjson( $obj->main_package );
    },
    components_list => sub {
        my $res = $obj->component_list;
        return join( "\n", sort keys %$res );
    },
    modules_list => sub {
        my $res = $obj->component_list;
        return join( "\n", sort values %$res );
    },
    normalize_name => sub {
        return $obj->normalize_name(@ARGV);
    },
    root_components_list => sub {
        my $res = $obj->root_components_list;
        return join( "\n", sort keys %$res );
    },
    packages_list => sub {
        return join( "\n", $obj->packages_list );
    },
    root_modules_list => sub {
        my $res = $obj->root_components_list;
        return join( "\n", sort values %$res );
    },
    ordered_components_list => sub {
        my @res = $obj->ordered_components_list;
        return join( "\n", @res );
    },
    pjson => sub {
        my $cmp = shift(@ARGV) || '.';
        my $res = $obj->pjson($cmp);
        while ( $_ = shift @ARGV ) {
            if ( ref $res ) {
                if ( defined $res->{$_} ) {
                    $res = $res->{$_};
                }
                else {
                    die "Unable to find $_ in $cmp/package.json" if (@ARGV);
                    $res = '';
                }
            }
            else {
                pod2usage(
                    -message => 'Bad json path',
                    -exitval => 3,
                );
            }
        }
        my $ref = ref($res) || '';
        return
            $ref eq 'HASH'  ? JSON::to_json($res)
          : $ref eq 'ARRAY' ? join( "\n", @$res )
          :                   $res;
    },
    main => sub {
        my $dir = '.';
        if ( -e 'debian/nodejs/main' ) {
            local $/ = undef;
            open my $f, 'debian/nodejs/main' or die $!;
            $dir = <$f>;
            close $f;
            $dir =~ s/\s//g;
        }
        return $obj->pjson($dir)->{name};
    },
    ln => sub {
        my $mod = shift @ARGV;
        die 'Missing arg' unless $mod;
        my $displayName = $mod;
        if ( $mod eq '-t' ) {
            $mod                          = shift @ARGV;
            $Debian::PkgJs::Utils::WANTTS = 1;
            $displayName                  = "(TS) $mod";
        }
        my $res = $obj->ln($mod);
        if ($res) {
            return $res < 0
              ? "$displayName exists"
              : "$displayName linked into node_modules";
        }
        print STDERR "Unable to link $mod: $!\n";
        exit 1;
    },
    link_build_modules => sub {
        $obj->link_build_modules;
        return '';
    },
    link_external_modules => sub {
        $obj->link_external_modules;
        return '';
    },
    link_internal_modules => sub {
        $obj->link_internal_modules;
        return '';
    },
    link_test_modules => sub {
        $obj->link_test_modules;
        return '';
    },
    clean_build_modules => sub {
        $obj->clean_build_modules;
        return '';
    },
    clean_external_modules => sub {
        $obj->clean_external_modules;
        return '';
    },
    clean_internal_modules => sub {
        $obj->clean_internal_modules;
        return '';
    },
    clean_test_modules => sub {
        $obj->clean_test_modules;
        return '';
    },
};

_sub->{'--version'} = _sub->{'-v'};
_sub->{'--help'}    = _sub->{'-h'};

if ( $cmd =~ /_list$/ ) {
    $OPTS->{lerna} =
      !search_bd_in_debian_control(qr/^dh-sequence-nodejs-no-lerna$/);
}
my $res = _sub->{$cmd}->();
chomp $res;
print $res. ( $res =~ /./ ? "\n" : '' );
__END__
=head1 NAME

pkgjs-utils - Nodejs package utilities

=head1 SYNOPSIS

  $ cd node-glob
  $ pkgjs-utils main
    # prints: glob
  $ pkgjs-main
    # alias for pkgjs-utils main
  $ pkgjs-utils components_list
    # prints:
    #  globalyzer
    #  globrex
    #  tiny-glob
    #  types-glob
  $ pkgjs-utils modules_list
    # prints:
    #  globalyzer
    #  globrex
    #  tiny-glob
    #  @types/glob
  $ pkgjs-utils pjson .
    # prints: { <package.json content, serialized> }
  $ pkgjs-pjson .
    # alias for pkgjs-utils pjson .
  $ cd ../node-jest
  $ pkgjs-pjson packages/jest-diff
    # prints: { <packages/jest-diff/package.json content, serialized> }
  $ pkgjs-pjson packages/jest-diff version
    # prints: 27.2.5
  $ pkgjs-utils pjson . scripts test
    # prints: jest
  $ pkgjs-utils pjson packages/jest-diff scripts test
  $ pkgjs-utils ln webpack
    # prints: 'webpack linked into node_modules
  $ pkgjs-utils ln @types/node
    # prints: '@types/node' linked into node_modules
  $ pkgjs-ln @types/node
    # same as pkgjs-utils ln @types/node
  $ pkgjs-utils normalize_name @types/node
    # prints: types-node
  $ pkgjs-utils root_components_list (only components installed in nodejs dir)
  $ pkgjs-utils root_modules_list (same with module names)
  $ pkgjs-utils ordered_components_list (components in build order)

=head1 DESCRIPTION

Collection of little commands to read <component>/package.json overridden by
debian/nodejs/<component>/* files.

=head2 Available commands

=over

=item B<link_test_modules>

Link modules that are in the B<debian/tests/test_modules> directory into
"node_modules".

=item B<link_external_modules>

Link Debian modules listed into B<debian/nodejs/extlinks> into "node_modules"
and copy modules listed into B<debian/nodejs/extcopies> into "node_modules".

=item B<link_internal_modules>

In multi-modules package I<(with B<uscan> components and/or lerna/workspace)>,
link these modules into "node_modules", except the "main" module.

=item B<link_build_modules>

Link modules that are in the B<debian/build_modules> directory into
"node_modules".

=item B<clean_test_modules>

Drop links created by B<link_test_modules>.

=item B<clean_external_modules>

Drop links/copies created by B<link_external_modules>.

=item B<clean_internal_modules>

Drop links created by B<link_internal_modules>.

=item B<clean_build_modules>

Drop links created by B<link_build_modules>.

=item B<ln>

Link the given Debian module into "node_modules".

If "-t" option is set, try to link the corresponding typescript declarations.
For example:

    $ pkgjs-ln -t glob; pkgjs-ln -t abab

creates "node_modules/@types/glob" and "node_modules/abab".

=item B<components_list>

Print the list of components (directories) without the "main" module.

=item B<modules_list>

Print the NPM names of components without the "main" module.

=item B<pjson>

Reads the package.json I<or package.yaml> of the given directory. If no
arguments are given, it prints the serialized JSON content. If arguments
are given, it tries to follow keys. For example:

    $ pkgjs-pjson packages/foo scripts run

write the content of the subkey "run" of the key "scripts".

=item B<main>

Print the NPM name of the "main" module.

=item B<packages_list>

Print the list of binary packages from "debian/control".

=item B<normalize_name>

Convert a NPM module name into a Debian compatible name. Examples:

    $ pkgjs-utils normalize_name @types/glob
    type-glob
    $ pkgjs-utils normalize_name @types/babel__type
    types-babel-type
    $ pkgjs-utils normalize_name require_optional
    require-optional

=item B<-h> B<--help>

=item B<-v> B<--version>

=back

=head1 AUTHOR

Yadd <yadd@debian.org>

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2021 by Yadd <yadd@debian.org>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
.
On Debian systems, the complete text of version 2 of the GNU General
Public License can be found in `/usr/share/common-licenses/GPL-2'

=cut
