package tasks

import (
	"errors"
	"fmt"
)

// Result structure of the task to provide methods
type Result struct {
	Result interface{} `json:"result"`
}

// Detailed information about task
type RecordAsyncTask struct {
	// Audit ID
	AuditID string `json:"auditId"`

	// Completed
	Completed bool `json:"completed"`

	// Error
	Error string `json:"error"`

	// List of logs
	Log []string `json:"log"`

	// Final Result
	Result

	// Stage
	Stage string `json:"stage"`

	// Status
	Status string `json:"status"`

	// Update time
	UpdateTime uint64 `json:"updateTime"`

	// Updated by
	UpdatedBy string `json:"updatedBy"`

	// Updated time
	UpdatedTime uint64 `json:"updatedTime"`

	// Time of creation
	CreatedTime uint64 `json:"createdTime"`
}

// Detailed information about task
type ItemAsyncTask struct {
	RecordAsyncTask
}

// List of tasks
type ListTasks struct {
	Data []ItemAsyncTask `json:"data"`

	EntryCount uint64 `json:"entryCount"`
}

// ID returns ID of cluster or WG or any other resource successfully created as a Result of the task.
// It returns error if Result does not contain any resource ID.
func (r Result) ID() (int, error) {
	// check id from cluster - it comes as slice, like [1234, "cluster-name"]
	slice, ok := r.Result.([]interface{})
	if ok {
		if len(slice) == 0 {
			return 0, fmt.Errorf("could not get ID from empty slice")
		}

		idFloat64, ok := slice[0].(float64)
		if !ok {
			return 0, fmt.Errorf("could not get ID from first slice element (%v)", slice[0])
		}

		return int(idFloat64), nil
	}

	// check id from other resources - it comes as id
	idFloat64, ok := r.Result.(float64)
	if ok {
		return int(idFloat64), nil
	}

	return 0, errors.New("could not get ID because result is neither slice nor number (%v)")
}

// Name returns name of cluster or WG successfully created as a Result of the task.
// It returns error if Result does not contain k8s name.
func (r Result) Name() (string, error) {
	slice, ok := r.Result.([]interface{})
	if !ok {
		return "", fmt.Errorf("could not convert Result (%v) to slice", r.Result)
	}

	if len(slice) < 2 {
		return "", fmt.Errorf("could not get name from second slice element")
	}

	var name string
	name, ok = slice[1].(string)
	if !ok {
		return "", fmt.Errorf("could not get name from second slice element (%v)", slice[1])
	}

	return name, nil
}

// ToMaps converts Result to a slice of maps containing back-up information as a result of the task.
// It returns error if Result does not contain back-up information.
func (r Result) ToMaps() ([]map[string]interface{}, error) {
	slice, ok := r.Result.([]interface{})
	if !ok {
		return nil, fmt.Errorf("could not convert Result (%v) to slice", r.Result)
	}

	if len(slice) == 0 {
		return nil, fmt.Errorf("could not get maps from empty slice")
	}

	result := make([]map[string]interface{}, 0, len(slice))
	for _, s := range slice {
		elem, ok := s.(map[string]interface{})
		if !ok {
			return nil, fmt.Errorf("could not get map[string]interface{} from slice element (%v)", s)
		}
		result = append(result, elem)
	}

	return result, nil
}

// ToString converts Result to non-empty string.
// It returns error if Result is not a string or is an empty string.
func (r Result) ToString() (string, error) {
	status, ok := r.Result.(string)
	if !ok {
		return "", fmt.Errorf("could not convert Result (%v) to string", r.Result)
	}

	if status == "" {
		return "", fmt.Errorf("info contains empty string")
	}

	return status, nil
}
