#!/usr/bin/env perl

use strict;
use warnings;

use File::Spec;
use FindBin qw($Bin);

# d2 is the first-class short entrypoint for the dashboard CLI. It is a real,
# installed command (shipped in the same bin directory as `dashboard`), not a
# shell alias, so it works in scripts and fresh shells without loading the shell
# bootstrap. It re-execs its sibling `dashboard` entrypoint with the same
# arguments so the two commands behave identically.
#
# DD-924: computed from $Bin here, not inline at the exec site below, because
# this whole file's body (this line included) is what PAX embeds verbatim
# into a compiled d2 binary - when THAT binary later runs, $Bin resolves via
# FindBin to wherever the compiled binary itself physically lives (PaxCache's
# cache directory), not to this source file's real bin/ directory, so
# recomputing this path from $Bin at that point silently breaks. Once the
# correct value is known (on this, the genuinely-interpreted run, where $Bin
# is accurate), it is baked into an env var by
# _maybe_exec_self_compiled_d2() before it execs into the compiled binary, so
# the compiled binary's own re-run of this same line reads that baked value
# instead of re-deriving a now-wrong one from its own $Bin.
my $dashboard = $ENV{DEVELOPER_DASHBOARD_D2_DASHBOARD_PATH}
    || File::Spec->catfile( $Bin, 'dashboard' );

_maybe_exec_self_compiled_d2();

exec { $^X } $^X, $dashboard, @ARGV;
die "Unable to exec dashboard entrypoint at $dashboard from d2: $!\n";

# _maybe_exec_self_compiled_d2()
# DD-882 (owner correction, Telegram msg #2001, 2026-09-15): d2 checks
# whether a fresh PAX-compiled binary is cached for ITS OWN source file
# (not dashboard's - the two are distinct compile targets sharing PaxCache's
# md5-of-path keying) and, if so, execs it directly with the original argv,
# exactly mirroring bin/dashboard's own _maybe_exec_self_compiled_dashboard.
# Without this, d2 would only ever benefit from a compiled DASHBOARD binary
# after paying for one extra interpreter hop (d2 -> perl dashboard -> exec);
# the owner named d2 explicitly and tested it directly, so it gets its own
# check rather than relying on that indirect path.
#
# Shares dashboard's DEVELOPER_DASHBOARD_PAX_SELF_EXECED guard rather than
# using a second variable, so the two entrypoints' hooks cannot double-fire
# on each other within one process tree: if dashboard's hook already exec'd
# a compiled binary (which embeds d2's own re-exec line unchanged, since PAX
# compiles the whole script body), d2's copy of this same check inside that
# compiled binary must not re-trigger either.
#
# Also skipped under $ENV{HARNESS_ACTIVE} for the identical reason documented
# on dashboard's own hook: a fresh per-test-block HOME must never trigger a
# real background compile with zero cache-reuse benefit.
# Input: none (reads @ARGV, $Bin, %ENV).
# Output: never returns on a successful self-exec; otherwise returns.
sub _maybe_exec_self_compiled_d2 {
    return if $ENV{HARNESS_ACTIVE};
    if ( !$ENV{DEVELOPER_DASHBOARD_PAX_SELF_EXECED} ) {
        my $self_path = File::Spec->catfile( $Bin, 'd2' );
        if ( -f $self_path ) {
            my $dashboard_lib = File::Spec->catdir( $Bin, '..', 'lib' );
            require lib;
            lib->import($dashboard_lib);
            require Developer::Dashboard::PathRegistry;
            require Developer::Dashboard::PaxCache;
            my $paths = Developer::Dashboard::PathRegistry->new(
                cwd             => ( eval { require Cwd; Cwd::cwd() } ),
                home            => $ENV{HOME},
                workspace_roots => [],
                project_roots   => [],
            );
            my $cache  = Developer::Dashboard::PaxCache->new( paths => $paths );
            my $cached = $cache->resolve($self_path);
            if ( defined $cached ) {
                $ENV{DEVELOPER_DASHBOARD_PAX_SELF_EXECED}   = 1;
                $ENV{DEVELOPER_DASHBOARD_D2_DASHBOARD_PATH} = $dashboard;
                exec { $cached } $cached, @ARGV;
                die "Unable to exec self-compiled d2 binary at $cached: $!\n";
            }
        }
    }
    delete $ENV{DEVELOPER_DASHBOARD_PAX_SELF_EXECED};
    delete $ENV{DEVELOPER_DASHBOARD_D2_DASHBOARD_PATH};
    return;
}

__END__

=head1 NAME

d2 - first-class short entrypoint for the Developer Dashboard CLI

=head1 SYNOPSIS

  d2 version
  d2 doctor
  d2 upgrade [--dry-run]
  d2 collector status housekeeper
  d2 ask "How do I list collectors?"

=head1 DESCRIPTION

C<d2> is a real, installed command that runs the same dashboard CLI as the
longer C<dashboard> command. It re-execs its sibling C<dashboard> entrypoint in
the same C<bin> directory with the same arguments, so anything you can do with
C<dashboard> you can do with C<d2>.

=for comment FULL-POD-DOC START

=head1 PURPOSE

This entrypoint gives operators a short, real command name for the dashboard CLI
that behaves exactly like C<dashboard>, so C<d2 version> or C<d2 collector
status> work the same as their C<dashboard> equivalents without typing the full
command name.

=head1 WHY IT EXISTS

It exists so C<d2> is a genuine installed command rather than only a shell
function defined by the shell bootstrap. As a real entrypoint in the same
C<bin> directory as C<dashboard>, C<d2> is available in non-interactive scripts,
cron jobs, and fresh shells that never sourced the dashboard shell integration,
which a shell-only alias could not guarantee.

=head1 WHEN TO USE

Use this file when changing how the short C<d2> command locates and re-enters
the main C<dashboard> entrypoint, or when adjusting how C<d2> is packaged as an
installed executable.

=head1 HOW TO USE

Run C<d2> followed by any dashboard subcommand and arguments, exactly as you
would run C<dashboard>. The script resolves its sibling C<dashboard> file from
its own install directory and re-execs it under the same Perl interpreter, so
the full command surface, hooks, and shell bootstrap output are identical.

=head1 WHAT USES IT

It is used by operators who prefer the short command name, by shell bootstrap
tab-completion that also completes C<d2>, and by CLI smoke tests that exercise
the short entrypoint.

=head1 EXAMPLES

Example 1:

  d2 version

Print the dashboard version through the short real command.

Example 2:

  d2 ask --codex "Explain this stack trace"

Run any dashboard subcommand through C<d2> exactly as through C<dashboard>.

Example 3:

  prove -lv t/05-cli-smoke.t t/40-install-bootstrap.t

Rerun the focused CLI-smoke and install-bootstrap tests after changing the short
entrypoint.

Example 4:

  prove -lr t

Verify the short entrypoint inside the complete repository suite.

=for comment FULL-POD-DOC END

=cut
