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
	ActiveMapfixStatuses = []model.MapfixStatus{
		model.MapfixStatusUploading,
		model.MapfixStatusValidated,
		model.MapfixStatusValidating,
		model.MapfixStatusAccepted,
		model.MapfixStatusChangesRequested,
		model.MapfixStatusSubmitted,
		model.MapfixStatusUnderConstruction,
	}
)

var(
	ErrActiveMapfixSameAssetID = errors.New("There is an active mapfix with the same AssetID")
	ErrNotAssetOwner = errors.New("You can only submit an asset you own")
)

// UpdateMapfixValidatedModel implements patchMapfixModel operation.
//
// Update model following role restrictions.
//
// POST /mapfixes/{MapfixID}/validated-model
func (svc *Service) UpdateMapfixValidatedModel(ctx context.Context, params internal.UpdateMapfixValidatedModelParams) 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.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusValidating}, pmap)
}

// ActionMapfixValidate invokes actionMapfixValidate operation.
//
// Role Validator changes status from Validating -> Validated.
//
// POST /mapfixes/{MapfixID}/status/validator-validated
func (svc *Service) ActionMapfixValidated(ctx context.Context, params internal.ActionMapfixValidatedParams) error {
	// transaction
	smap := datastore.Optional()
	smap.Add("status_id", model.MapfixStatusValidated)
	return svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusValidating}, smap)
}

// ActionMapfixAccepted implements actionMapfixAccepted operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Accepted.
//
// POST /mapfixes/{MapfixID}/status/validator-failed
func (svc *Service) ActionMapfixAccepted(ctx context.Context, params internal.ActionMapfixAcceptedParams) error {
	// transaction
	smap := datastore.Optional()
	smap.Add("status_id", model.MapfixStatusAccepted)
	smap.Add("status_message", params.StatusMessage)
	return svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusValidating}, smap)
}

// ActionMapfixUploaded implements actionMapfixUploaded operation.
//
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.
//
// POST /mapfixes/{MapfixID}/status/validator-uploaded
func (svc *Service) ActionMapfixUploaded(ctx context.Context, params internal.ActionMapfixUploadedParams) error {
	// transaction
	smap := datastore.Optional()
	smap.Add("status_id", model.MapfixStatusUploaded)
	return svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusUploading}, smap)
}

// POST /mapfixes
func (svc *Service) CreateMapfix(ctx context.Context, request *internal.MapfixCreate) (*internal.MapfixID, error) {
	// Check if an active mapfix 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", ActiveMapfixStatuses)
		active_mapfixes, err := svc.DB.Mapfixes().List(ctx, filter, model.Page{
			Number: 1,
			Size:   1,
		},datastore.ListSortDisabled)
		if err != nil {
			return nil, err
		}
		if len(active_mapfixes) != 0{
			return nil, ErrActiveMapfixSameAssetID
		}
	}

	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
	}

	mapfix, err := svc.DB.Mapfixes().Create(ctx, model.Mapfix{
		ID:            0,
		DisplayName:   request.DisplayName,
		Creator:       request.Creator,
		GameID:        request.GameID,
		Submitter:     request.AssetOwner,
		AssetID:       request.AssetID,
		AssetVersion:  request.AssetVersion,
		Completed:     false,
		TargetAssetID: request.TargetAssetID,
		StatusID:      model.MapfixStatusUnderConstruction,
	})
	if err != nil {
		return nil, err
	}
	return &internal.MapfixID{
		MapfixID: mapfix.ID,
	}, nil
}