package service_internal

import (
	"context"
	"errors"
	"fmt"

	"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) {
	// sanitization
	if request.GameID<0||
	request.AssetOwner<0||
	request.AssetID<0||
	request.AssetVersion<0||
	request.TargetAssetID<0{
		return nil, ErrNegativeID
	}
	var GameID=uint32(request.GameID);
	var Submitter=uint64(request.AssetOwner);
	var AssetID=uint64(request.AssetID);
	var AssetVersion=uint64(request.AssetVersion);
	var TargetAssetID=uint64(request.TargetAssetID);

	// 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:        GameID,
		Submitter:     Submitter,
		AssetID:       AssetID,
		AssetVersion:  AssetVersion,
		Completed:     false,
		TargetAssetID: TargetAssetID,
		StatusID:      model.MapfixStatusUnderConstruction,
	})
	if err != nil {
		return nil, err
	}

	// mark the operation as completed and provide the path
	pmap := datastore.Optional()
	pmap.Add("status_id", model.OperationStatusCompleted)
	pmap.Add("path", fmt.Sprintf("/mapfixes/%d", mapfix.ID))
	err = svc.DB.Operations().Update(ctx, request.OperationID, pmap)
	if err != nil {
		return nil, err
	}

	return &internal.MapfixID{
		MapfixID: mapfix.ID,
	}, nil
}