use strict;
use warnings;
use Setup;
use Test2::V0;
use Catalyst::Test ();
use HTTP::Request;
use HTTP::Request::Common;
use JSON::MaybeXS qw(decode_json encode_json);

sub is_json {
    my ($response, $message) = @_;

    my $data;
    my $valid_json = lives { $data = decode_json($response->content); };
    ok($valid_json, $message // "We get back valid JSON.");
    if (!$valid_json) {
        use Data::Dumper;
        print STDERR Dumper $response->content;
    }

    return $data;
}

my $ctx = test_context();

Catalyst::Test->import('Hydra');

my $finishedBuilds = $ctx->makeAndEvaluateJobset(
    expression => "one-job.nix",
    build => 1
);

my $queuedBuilds = $ctx->makeAndEvaluateJobset(
    expression => "one-job.nix",
    build => 0
);

subtest "/api/queue" => sub {
    my $response = request(GET '/api/queue?nr=1');
    ok($response->is_success, "The API enpdoint showing the queue returns 200.");

    my $data = is_json($response);
    my $build = $queuedBuilds->{"one_job"};
    like($data, [{
        priority => $build->priority,
        id => $build->id,
    }]);
};

subtest "/api/latestbuilds" => sub {
    subtest "with no specific parameters" => sub {
        my $response = request(GET '/api/latestbuilds?nr=1');
        ok($response->is_success, "The API enpdoint showing the latest builds returns 200.");

        my $data = is_json($response);
        my $build = $finishedBuilds->{"one_job"};
        like($data, [{
            buildstatus => $build->buildstatus,
            id => $build->id,
        }]);
    };

    subtest "with very specific parameters" => sub {
        my $build = $finishedBuilds->{"one_job"};
        my $projectName = $build->project->name;
        my $jobsetName = $build->jobset->name;
        my $jobName = $build->job;
        my $system = $build->system;
        my $response = request(GET "/api/latestbuilds?nr=1&project=$projectName&jobset=$jobsetName&job=$jobName&system=$system");
        ok($response->is_success, "The API enpdoint showing the latest builds returns 200.");

        my $data = is_json($response);

        like($data, [{
            buildstatus => $build->buildstatus,
            id => $build->id,
        }]);
    };
};

subtest "/api/nrbuilds" => sub {
    subtest "with no specific parameters" => sub {
        my $response = request(GET '/api/nrbuilds?nr=1&period=hour');
        ok($response->is_success, "The API enpdoint showing the latest builds returns 200.");

        my $data = is_json($response);
        is($data, [1]);
    };

    subtest "with very specific parameters" => sub {
        my $build = $finishedBuilds->{"one_job"};
        my $projectName = $build->project->name;
        my $jobsetName = $build->jobset->name;
        my $jobName = $build->job;
        my $system = $build->system;
        my $response = request(GET "/api/nrbuilds?nr=1&period=hour&project=$projectName&jobset=$jobsetName&job=$jobName&system=$system");
        ok($response->is_success, "The API enpdoint showing the latest builds returns 200.");

        my $data = is_json($response);
        is($data, [1]);
    };
};

subtest "/api/push" => sub {
    subtest "with a specific jobset" => sub {
        my $build = $finishedBuilds->{"one_job"};
        my $jobset = $build->jobset;
        my $projectName = $jobset->project->name;
        my $jobsetName = $jobset->name;
        is($jobset->forceeval, undef, "The existing jobset is not set to be forced to eval");

        my $response = request(GET "/api/push?jobsets=$projectName:$jobsetName&force=1");
        ok($response->is_success, "The API enpdoint for triggering jobsets returns 200.");

        my $data = is_json($response);
        is($data, { jobsetsTriggered => [ "$projectName:$jobsetName" ] });

        my $updatedJobset = $ctx->db->resultset('Jobsets')->find({ id => $jobset->id });
        is($updatedJobset->forceeval, 1, "The jobset is now forced to eval");
    };

    subtest "with a specific source" => sub {
        my $repo = $ctx->jobsdir;
        my $jobsetA = $queuedBuilds->{"one_job"}->jobset;
        my $jobsetB = $finishedBuilds->{"one_job"}->jobset;

        is($jobsetA->forceeval, undef, "The existing jobset is not set to be forced to eval");

        print STDERR $repo;

        my $response = request(GET "/api/push?repos=$repo&force=1");
        ok($response->is_success, "The API enpdoint for triggering jobsets returns 200.");

        my $data = is_json($response);
        is($data, { jobsetsTriggered => [
            "${\$jobsetA->project->name}:${\$jobsetA->name}",
            "${\$jobsetB->project->name}:${\$jobsetB->name}"
        ] });

        my $updatedJobset = $ctx->db->resultset('Jobsets')->find({ id => $jobsetA->id });
        is($updatedJobset->forceeval, 1, "The jobset is now forced to eval");
    };
};

subtest "/api/push-github" => sub {
    # Create a project and jobset which looks like it comes from GitHub
    my $user = $ctx->db()->resultset('Users')->create({
        username => "api-push-github",
        emailaddress => 'api-push-github@example.org',
        password => ''
    });

    my $project = $ctx->db()->resultset('Projects')->create({
        name => "api-push-github",
        displayname => "api-push-github",
        owner => $user->username
    });

    subtest "with a legacy input type" => sub {
        my $jobset = $project->jobsets->create({
            name => "legacy-input-type",
            nixexprinput => "src",
            nixexprpath => "default.nix",
            emailoverride => ""
        });

        my $jobsetinput = $jobset->jobsetinputs->create({name => "src", type => "git"});
        $jobsetinput->jobsetinputalts->create({altnr => 0, value => "https://github.com/OWNER/LEGACY-REPO.git"});

        my $req = POST '/api/push-github',
            "Content-Type" => "application/json",
            "Content" => encode_json({
                repository => {
                    owner => {
                        name => "OWNER",
                    },
                    name => "LEGACY-REPO",
                }
            });

        my $response = request($req);
        ok($response->is_success, "The API enpdoint for triggering jobsets returns 200.");

        my $data = is_json($response);
        is($data, { jobsetsTriggered => [ "api-push-github:legacy-input-type" ] }, "The correct jobsets are triggered.");
    };

    subtest "with a flake input type" => sub {
        my $jobset = $project->jobsets->create({
            name => "flake-input-type",
            type => 1,
            flake => "github:OWNER/FLAKE-REPO",
            emailoverride => ""
        });

        my $req = POST '/api/push-github',
            "Content-Type" => "application/json",
            "Content" => encode_json({
                repository => {
                    owner => {
                        name => "OWNER",
                    },
                    name => "FLAKE-REPO",
                }
            });

        my $response = request($req);
        ok($response->is_success, "The API enpdoint for triggering jobsets returns 200.");

        my $data = is_json($response);
        is($data, { jobsetsTriggered => [ "api-push-github:flake-input-type" ] }, "The correct jobsets are triggered.");
    };
};

done_testing;