package Hydra::Controller::Project;

use utf8;
use strict;
use warnings;
use base 'Hydra::Base::Controller::ListBuilds';
use Hydra::Helper::Nix;
use Hydra::Helper::CatalystUtils;


sub projectChain :Chained('/') :PathPart('project') :CaptureArgs(1) {
    my ($self, $c, $projectName) = @_;
    $c->stash->{params}->{name} //= $projectName;

    my $isCreate = $c->action->name eq "project" && $c->request->method eq "PUT";

    $c->stash->{project} = $c->model('DB::Projects')->find($projectName);

    $c->stash->{isProjectOwner} = !$isCreate && isProjectOwner($c, $c->stash->{project});

    notFound($c, "Project ‘$projectName’ doesn't exist.")
        if !$c->stash->{project} && !$isCreate;
}


sub project :Chained('projectChain') :PathPart('') :Args(0) :ActionClass('REST::ForBrowsers') { }

sub project_GET {
    my ($self, $c) = @_;

    $c->stash->{template} = 'project.tt';

    $c->stash->{jobsets} = [jobsetOverview($c, $c->stash->{project})];

    $self->status_ok($c, entity => $c->stash->{project});
}

sub project_PUT {
    my ($self, $c) = @_;

    if (defined $c->stash->{project}) {
        requireProjectOwner($c, $c->stash->{project});

        $c->model('DB')->schema->txn_do(sub {
            updateProject($c, $c->stash->{project});
        });

        my $uri = $c->uri_for($self->action_for("project"), [$c->stash->{project}->name]) . "#tabs-configuration";
        $self->status_ok($c, entity => { redirect => "$uri" });

        $c->flash->{successMsg} = "The project configuration has been updated.";
    }

    else {
        requireMayCreateProjects($c);

        my $project;
        $c->model('DB')->schema->txn_do(sub {
            # Note: $projectName is validated in updateProject,
            # which will abort the transaction if the name isn't
            # valid.  Idem for the owner.
            my $owner = $c->user->username;
            $project = $c->model('DB::Projects')->create(
                { name => ".tmp", displayname => "", owner => $owner });
            updateProject($c, $project);
        });

        my $uri = $c->uri_for($self->action_for("project"), [$project->name]);
        $self->status_created($c,
            location => "$uri",
            entity => { name => $project->name, uri => "$uri", redirect => "$uri", type => "project" });
    }
}

sub project_DELETE {
    my ($self, $c) = @_;

    requireProjectOwner($c, $c->stash->{project});

    $c->model('DB')->schema->txn_do(sub {
        $c->stash->{project}->builds->delete;
        $c->stash->{project}->jobsets->delete;
        $c->stash->{project}->delete;
    });

    my $uri = $c->res->redirect($c->uri_for("/"));
    $self->status_ok($c, entity => { redirect => "$uri" });

    $c->flash->{successMsg} = "The project has been deleted.";
}


sub edit : Chained('projectChain') PathPart Args(0) {
    my ($self, $c) = @_;

    requireProjectOwner($c, $c->stash->{project});

    $c->stash->{template} = 'edit-project.tt';
    $c->stash->{edit} = 1;
}


sub requireMayCreateProjects {
    my ($c) = @_;
    requireUser($c);
    accessDenied($c, "Only administrators or authorised users can perform this operation.")
        unless $c->check_user_roles('admin') || $c->check_user_roles('create-projects');
}


sub create : Path('/create-project') {
    my ($self, $c) = @_;

    requireMayCreateProjects($c);

    $c->stash->{template} = 'edit-project.tt';
    $c->stash->{create} = 1;
}


sub create_jobset : Chained('projectChain') PathPart('create-jobset') Args(0) {
    my ($self, $c) = @_;

    requireProjectOwner($c, $c->stash->{project});

    $c->stash->{template} = 'edit-jobset.tt';
    $c->stash->{create} = 1;
    $c->stash->{totalShares} = getTotalShares($c->model('DB')->schema);
    $c->stash->{emailNotification} = $c->config->{email_notification} // 0;
}


sub updateProject {
    my ($c, $project) = @_;

    my $owner = $project->owner;
    if ($c->check_user_roles('admin') and defined $c->stash->{params}->{owner}) {
        $owner = trim $c->stash->{params}->{owner};
        badRequest($c, "The user name ‘$owner’ does not exist.")
            unless defined $c->model('DB::Users')->find($owner);
    }

    my $projectName = $c->stash->{params}->{name};
    error($c, "Invalid project identifier ‘$projectName’.") if $projectName !~ /^$projectNameRE$/;

    error($c, "Cannot rename project to ‘$projectName’ since that identifier is already taken.")
        if $projectName ne $project->name && defined $c->model('DB::Projects')->find($projectName);

    my $displayName = trim $c->stash->{params}->{displayname};
    error($c, "You must specify a display name.") if $displayName eq "";

    my $enable_dynamic_run_command = defined $c->stash->{params}->{enable_dynamic_run_command} ? 1 : 0;
    if ($enable_dynamic_run_command && !$c->config->{dynamicruncommand}->{enable}) {
        badRequest($c, "Dynamic RunCommand is not enabled by the server.");
    }

    $project->update(
        { name => $projectName
        , displayname => $displayName
        , description => trim($c->stash->{params}->{description})
        , homepage => trim($c->stash->{params}->{homepage})
        , enabled => defined $c->stash->{params}->{enabled} ? 1 : 0
        , hidden => defined $c->stash->{params}->{visible} ? 0 : 1
        , owner => $owner
        , enable_dynamic_run_command => $enable_dynamic_run_command
        , declfile => trim($c->stash->{params}->{declarative}->{file})
        , decltype => trim($c->stash->{params}->{declarative}->{type})
        , declvalue => trim($c->stash->{params}->{declarative}->{value})
        });
    if (length($project->declfile)) {
        # This logic also exists in the DeclarativeJobets tests.
        # TODO: refactor and deduplicate.
        $project->jobsets->update_or_create(
            { name=> ".jobsets"
            , nixexprinput => ""
            , nixexprpath => ""
            , emailoverride => ""
            , triggertime => time
            });
    } else {
        $project->jobsets->search({ name => ".jobsets" })->delete;
        $project->update(
            { decltype => ""
            , declvalue => ""
            });
    }
}


# Hydra::Base::Controller::ListBuilds needs this.
sub get_builds : Chained('projectChain') PathPart('') CaptureArgs(0) {
    my ($self, $c) = @_;
    $c->stash->{allBuilds} = $c->stash->{project}->builds;
    $c->stash->{latestSucceeded} = $c->model('DB')->resultset('LatestSucceededForProject')
        ->search({}, {bind => [$c->stash->{project}->name]});
    $c->stash->{channelBaseName} = $c->stash->{project}->name;
}


1;
