# Creates a merge request from current directory (or using parameters)
package Devscripts::Salsa::merge_request;

use strict;
use Devscripts::Output;
use Dpkg::IPC;
use Moo::Role;

with 'Devscripts::Salsa::search_project';

sub merge_request {
    my ($self, $dst_project, $dst_branch) = @_;
    my $src_branch  = $self->config->mr_src_branch;
    my $src_project = $self->config->mr_src_project;
    $dst_project ||= $self->config->mr_dst_project;
    $dst_branch  ||= $self->config->mr_dst_branch;
    my $title = $self->config->mr_title;
    my $desc  = $self->config->mr_desc;

    if ($src_branch) {
        unless ($src_project and $dst_project) {
            ds_warn "--mr-src-project and --mr-src-project "
              . "are required when --mr-src-branch is set";
            return 1;
        }
        unless ($src_project =~ m#/#) {
            $src_project = $self->project2path($src_project);
        }
    } else {    # Use current repository to find elements
        ds_verbose "using current branch as source";
        my $out;
        unless ($src_project) {
            # 1. Verify that repo is ready
            spawn(
                exec       => [qw(git status -s -b -uno)],
                wait_child => 1,
                to_string  => \$out
            );
            chomp $out;
            # Case "rebased"
            if ($out =~ /\[/) {
                ds_warn "Current branch isn't pushed, aborting:\n";
                return 1;
            }
            # Case else: nothing after src...dst
            unless ($out =~ /\s(\S+)\.\.\.(\S+)/s) {
                ds_warn
                  "Current branch as no origin or isn't pushed, aborting";
                return 1;
            }
            # 2. Set source branch to current branch
            $src_branch ||= $1;
            ds_verbose "Found current branch: $src_branch";
        }
        unless ($src_project and $dst_project) {
            # Check remote links
            spawn(
                exec       => [qw(git remote --verbose show)],
                wait_child => 1,
                to_string  => \$out,
            );
            my $origin = $self->config->api_url;
            $origin =~ s#api/v4$##;
            # 3. Set source project using "origin" target
            unless ($src_project) {
                if ($out
                    =~ /origin\s+(?:\Q$self->{config}->{git_server_url}\E|\Q$origin\E)(\S*)/m
                ) {
                    $src_project = $1;
                    $src_project =~ s/\.git$//;
                } else {
                    ds_warn
"Unable to find project origin, set it using --mr-src-project";
                    return 1;
                }
            }
            # 4. Steps to find destination project:
            #    - command-line
            #    - GitLab API (search for "forked_from_project"
            #    - "upstream" in git remote
            #    - use source project as destination project

            # 4.1. Stop if dest project has been given in command line
            unless ($dst_project) {
                my $project = $self->api->project($src_project);

                # 4.2. Search original project from GitLab API
                if ($project->{forked_from_project}) {
                    $dst_project
                      = $project->{forked_from_project}->{path_with_namespace};
                }
                if ($dst_project) {
                    ds_verbose "Project was forked from $dst_project";

                    # 4.3. Search for an "upstream" target in `git remote`
                } elsif ($out
                    =~ /upstream\s+(?:\Q$self->{config}->{git_server_url}\E|\Q$origin\E)(\S*)/m
                ) {
                    $dst_project = $1;
                    $dst_project =~ s/\.git$//;
                    ds_verbose 'Use "upstream" target as dst project';
                    # 4.4. Use source project as destination
                } else {
                    ds_warn
"No upstream target found, using current project as target";
                    $dst_project = $src_project;
                }
                ds_verbose "Use $dst_project as dest project";
            }
        }
        # 5. Search for MR title and desc
        unless ($title) {
            ds_warn "Title not set, using last commit";
            spawn(
                exec       => ['git', 'show', '--format=format:%s###%b'],
                wait_child => 1,
                to_string  => \$out,
            );
            $out =~ s/\ndiff.*$//s;
            my ($t, $d) = split /###/, $out;
            chomp $d;
            $title = $t;
            ds_verbose "Title set to $title";
            $desc ||= $d;
            # Replace all bug links by markdown links
            if ($desc) {
                $desc =~ s@#(\d{6,})\b@[#$1](https://bugs.debian.org/$1)@mg;
                ds_verbose "Desc set to $desc";
            }
        }
    }
    if ($dst_project eq 'same') {
        $dst_project = $src_project;
    }
    my $src = $self->api->project($src_project);
    unless ($title) {
        ds_warn "Title is required";
        return 1;
    }
    unless ($src and $src->{id}) {
        ds_warn "Target project not found $src_project";
        return 1;
    }
    my $dst;
    if ($dst_project) {
        $dst = $self->api->project($dst_project);
        unless ($dst and $dst->{id}) {
            ds_warn "Target project not found";
            return 1;
        }
    }
    return 1
      if (
        ds_prompt(
"You're going to push an MR to $dst_project:$dst_branch. Continue (Y/n)"
        ) =~ refuse
      );
    my $res = $self->api->create_merge_request(
        $src->{id},
        {
            source_branch        => $src_branch,
            target_branch        => $dst_branch,
            title                => $title,
            remove_source_branch => $self->config->mr_remove_source_branch,
            squash               => $self->config->mr_allow_squash,
            ($dst  ? (target_project_id => $dst->{id}) : ()),
            ($desc ? (description       => $desc)      : ()),
        });
    ds_warn "MR '$title' posted:";
    ds_warn $res->{web_url};
    return 0;
}

1;
