package System::Introspector::Report::Builder::Repositories::Git::Locations;
use Moo;

extends 'System::Introspector::Report::Builder';

has collect_matching        => (is => 'ro', default => sub { [] });
has _locations_by_origin    => (is => 'lazy', default => sub { {} });

sub required_data {
  return qw(
    repositories/git
    host
  );
}

sub _match_origin {
  my ($self, $origin) = @_;
  my @match_rx = @{ $self->collect_matching };
  for my $rx (@match_rx) {
    if ($origin =~ qr{^$rx$}i) {
      return $1;
    }
  }
  return undef;
}

my $_trim = sub {
    my ($string) = @_;
    $string =~ s{^\s*}{}g;
    $string =~ s{\s*$}{}g;
    return $string;
};

my $_format_commit = sub {
    my ($current) = @_;
    my @lines = @{ $current->{commit} || [] };
    return undef
        unless @lines;
    (my $commit = shift @lines) =~ s{^commit\s+}{}i;
    my %header;
    HEADER: while (my $line = shift @lines) {
        if ($line =~ m{^([^:]+):\s*(.+)$}) {
            $header{$1} = $2;
        }
        elsif ($line =~ m{^\s*$}) {
            last HEADER;
        }
        else {
            return 'Unable to parse message headers';
        }
    }
    my $title = '';
    while (my $line = shift @lines) {
        if ($line =~ m{\S}) {
            $title = $line;
        }
    }
    return join("\n",
        $commit->$_trim,
        sprintf('by %s', $header{Author} || ''),
        sprintf('at %s', $header{CommitDate} || ''),
        $title->$_trim,
    );
};

my $_commit_count = sub {
    my ($data) = @_;
    my $branch = $data->{current}{branch};
    return undef
        unless defined $branch;
    if (my $cnt = $data->{tracked}{branches}{$branch}{local_commit_count}) {
        return $cnt
            if defined($cnt) and not(ref $cnt);
    }
    if (my $cnt = $data->{current}{origin}{local_commit_count}) {
        return $cnt
            if defined($cnt) and not(ref $cnt);
    }
    return undef;
};

sub collect_from {
  my ($self, $remote, $data) = @_;
  my $git = $data->{'repositories/git'}{git} || {};
  for my $location (keys %$git) {
    my $origin  = $git->{$location}{config}{contents}{'remote.origin.url'}
      or next;
    my $matched = $self->_match_origin($origin)
      or next;
    my $current = $git->{$location}{current} || {};
    push @{$self->_locations_by_origin->{$matched}}, {
      remote        => $remote,
      hostname      => $data->{host}{hostname},
      location      => $location,
      origin        => $origin,
      local_count   => $git->{$location}->$_commit_count,
      branch        => $current->{branch},
      last_commit   => $current->$_format_commit,
    };
  }
  return 1;
}

sub render_reports {
  my ($self) = @_;
  my @columns = (
    { key => 'remote',      label => 'Remote Host' },
    { key => 'hostname',    label => 'Hostname' },
    { key => 'location',    label => 'Location' },
    { key => 'branch',      label => 'Branch' },
    { key => 'local_count', label => 'Changes' },
    { key => 'last_commit', label => 'Last Commit' },
  );
  my $collected = $self->_locations_by_origin;
  return map {
    my $identifier  = $_;
    my $rows        = $collected->{$identifier};
    {
      columns   => [@columns],
      title     => "$identifier Checkouts",
      id        => ['repositories-git-locations', $identifier],
      rowid     => [qw( remote location )],
      meta      => { repository => $identifier },
      rows      => [
        sort {
          ($a->{remote} cmp $b->{remote})
            ||
          ($a->{location} cmp $b->{location})
        } @$rows,
      ],
    };
  } keys %$collected;
}

1;
