package target

import (
	"encoding/json"
	"errors"
	"time"

	"github.com/google/uuid"
	"github.com/osbuild/osbuild-composer/internal/common"
)

type Target struct {
	Uuid      uuid.UUID              `json:"uuid"`
	ImageName string                 `json:"image_name"` // Desired name of the image in the target environment
	Name      string                 `json:"name"`       // Name of the specific target type
	Created   time.Time              `json:"created"`
	Status    common.ImageBuildState `json:"status"`
	Options   TargetOptions          `json:"options"` // Target type specific options
}

func newTarget(name string, options TargetOptions) *Target {
	return &Target{
		Uuid:    uuid.New(),
		Name:    name,
		Created: time.Now(),
		Status:  common.IBWaiting,
		Options: options,
	}
}

type TargetOptions interface {
	isTargetOptions()
}

type rawTarget struct {
	Uuid      uuid.UUID              `json:"uuid"`
	ImageName string                 `json:"image_name"`
	Name      string                 `json:"name"`
	Created   time.Time              `json:"created"`
	Status    common.ImageBuildState `json:"status"`
	Options   json.RawMessage        `json:"options"`
}

func (target *Target) UnmarshalJSON(data []byte) error {
	var rawTarget rawTarget
	err := json.Unmarshal(data, &rawTarget)
	if err != nil {
		return err
	}
	options, err := UnmarshalTargetOptions(rawTarget.Name, rawTarget.Options)
	if err != nil {
		return err
	}

	target.Uuid = rawTarget.Uuid
	target.ImageName = rawTarget.ImageName
	target.Name = rawTarget.Name
	target.Created = rawTarget.Created
	target.Status = rawTarget.Status
	target.Options = options

	return nil
}

func UnmarshalTargetOptions(targetName string, rawOptions json.RawMessage) (TargetOptions, error) {
	var options TargetOptions
	switch targetName {
	case "org.osbuild.azure":
		options = new(AzureTargetOptions)
	case "org.osbuild.aws":
		options = new(AWSTargetOptions)
	case "org.osbuild.aws.s3":
		options = new(AWSS3TargetOptions)
	case "org.osbuild.gcp":
		options = new(GCPTargetOptions)
	case "org.osbuild.azure.image":
		options = new(AzureImageTargetOptions)
	case "org.osbuild.local":
		options = new(LocalTargetOptions)
	case "org.osbuild.koji":
		options = new(KojiTargetOptions)
	case "org.osbuild.vmware":
		options = new(VMWareTargetOptions)
	case "org.osbuild.oci":
		options = new(OCITargetOptions)
	case "org.osbuild.container":
		options = new(ContainerTargetOptions)
	default:
		return nil, errors.New("unexpected target name")
	}
	err := json.Unmarshal(rawOptions, options)

	return options, err
}
