package service_internal

import (
	"context"
	"errors"

	"git.itzana.me/strafesnet/maps-service/pkg/datastore"
	internal "git.itzana.me/strafesnet/maps-service/pkg/internal"
	"git.itzana.me/strafesnet/maps-service/pkg/model"
)

var(
	// prevent two mapfixes with same asset id
	ActiveSubmissionStatuses = []model.SubmissionStatus{
		model.SubmissionStatusUploading,
		model.SubmissionStatusValidated,
		model.SubmissionStatusValidating,
		model.SubmissionStatusAccepted,
		model.SubmissionStatusChangesRequested,
		model.SubmissionStatusSubmitted,
		model.SubmissionStatusUnderConstruction,
	}
)

var(
	ErrActiveSubmissionSameAssetID = errors.New("There is an active submission with the same AssetID")
)

// UpdateSubmissionValidatedModel implements patchSubmissionModel operation.
//
// Update model following role restrictions.
//
// POST /submissions/{SubmissionID}/validated-model
func (svc *Service) UpdateSubmissionValidatedModel(ctx context.Context, params internal.UpdateSubmissionValidatedModelParams) error {
	// check if Status is ChangesRequested|Submitted|UnderConstruction
	pmap := datastore.Optional()
	pmap.AddNotNil("validated_asset_id", params.ValidatedModelID)
	pmap.AddNotNil("validated_asset_version", params.ValidatedModelVersion)
	// DO NOT reset completed when validated model is updated
	// pmap.Add("completed", false)
	return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, pmap)
}

// ActionSubmissionValidate invokes actionSubmissionValidate operation.
//
// Role Validator changes status from Validating -> Validated.
//
// POST /submissions/{SubmissionID}/status/validator-validated
func (svc *Service) ActionSubmissionValidated(ctx context.Context, params internal.ActionSubmissionValidatedParams) error {
	// transaction
	smap := datastore.Optional()
	smap.Add("status_id", model.SubmissionStatusValidated)
	return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, smap)
}

// ActionSubmissionAccepted implements actionSubmissionAccepted operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Accepted.
//
// POST /submissions/{SubmissionID}/status/validator-failed
func (svc *Service) ActionSubmissionAccepted(ctx context.Context, params internal.ActionSubmissionAcceptedParams) error {
	// transaction
	smap := datastore.Optional()
	smap.Add("status_id", model.SubmissionStatusAccepted)
	smap.Add("status_message", params.StatusMessage)
	return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, smap)
}

// ActionSubmissionUploaded implements actionSubmissionUploaded operation.
//
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.
//
// POST /submissions/{SubmissionID}/status/validator-uploaded
func (svc *Service) ActionSubmissionUploaded(ctx context.Context, params internal.ActionSubmissionUploadedParams) error {
	// transaction
	smap := datastore.Optional()
	smap.Add("status_id", model.SubmissionStatusUploaded)
	smap.Add("uploaded_asset_id", params.UploadedAssetID)
	return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusUploading}, smap)
}

// POST /submissions
func (svc *Service) CreateSubmission(ctx context.Context, request *internal.SubmissionCreate) (*internal.ID, error) {
	// Check if an active submission with the same asset id exists
	{
		filter := datastore.Optional()
		filter.Add("asset_id", request.AssetID)
		filter.Add("asset_version", request.AssetVersion)
		filter.Add("status_id", ActiveSubmissionStatuses)
		active_submissions, err := svc.DB.Submissions().List(ctx, filter, model.Page{
			Number: 1,
			Size:   1,
		},datastore.ListSortDisabled)
		if err != nil {
			return nil, err
		}
		if len(active_submissions) != 0{
			return nil, ErrActiveSubmissionSameAssetID
		}
	}

	operation, err := svc.DB.Operations().Get(ctx, request.OperationID)
	if err != nil {
		return nil, err
	}

	// check if user owns asset
	// TODO: allow bypass by admin
	if operation.Owner != request.AssetOwner {
		return nil, ErrNotAssetOwner
	}

	submission, err := svc.DB.Submissions().Create(ctx, model.Submission{
		ID:            0,
		DisplayName:   request.DisplayName,
		Creator:       request.Creator,
		GameID:        request.GameID,
		Submitter:     request.AssetOwner,
		AssetID:       request.AssetID,
		AssetVersion:  request.AssetVersion,
		Completed:     false,
		StatusID:      model.SubmissionStatusUnderConstruction,
	})
	if err != nil {
		return nil, err
	}
	return &internal.ID{
		ID: submission.ID,
	}, nil
}