package export

import (
	"fmt"
	"time"

	apiv1 "github.com/tidbcloud/cluster-service-ng/api/cluster-service-ng/v1beta"
	"github.com/tidbcloud/cluster-service-ng/domain/entity"
	"github.com/tidbcloud/cluster-service-ng/pkg/storage/mysql/schema"
	"google.golang.org/protobuf/encoding/protojson"
	"google.golang.org/protobuf/types/known/timestamppb"
)

func (r *exportRepoImpl) cvtExportToModel(in *entity.Export) (*schema.Export, error) {
	if in == nil {
		return nil, fmt.Errorf("export is nil")
	}

	exportOptionsBytes, err := protojson.Marshal(in.GetExportOptions())
	if err != nil {
		return nil, fmt.Errorf("failed to marshal export options: %w", err)
	}
	cloudStorageBytes, err := protojson.Marshal(in.GetCloudStorage())
	if err != nil {
		return nil, fmt.Errorf("failed to marshal cloud storage: %w", err)
	}

	var snapshotTime *time.Time
	if in.GetSnapshotTime() != nil {
		v := in.GetSnapshotTime().AsTime()
		snapshotTime = &v
	}

	var completeTime *time.Time
	if in.GetCompleteTime() != nil {
		v := in.GetCompleteTime().AsTime()
		completeTime = &v
	}

	var createTime time.Time
	if in.GetCreateTime() != nil {
		createTime = in.GetCreateTime().AsTime()
	} else {
		createTime = time.Now()
	}

	return &schema.Export{
		ExportID:        in.GetExportId(),
		ClusterID:       in.GetClusterId(),
		State:           fromProtoExportState(in.GetState()),
		ExportOptions:   string(exportOptionsBytes),
		CloudStorage:    string(cloudStorageBytes),
		Reason:          in.GetReason(),
		SnapshotTSO:     in.GetSnapshotTso(),
		SnapshotTime:    snapshotTime,
		Username:        in.GetUsername(),
		Password:        in.GetPassword(),
		TiDBGroupID:     in.GetTidbGroupId(),
		DumplingJobName: in.GetDumplingJobName(),
		CompleteTime:    completeTime,
		CreatedAt:       createTime,
	}, nil
}

func (r *exportRepoImpl) cvtModelToExport(e *schema.Export) (*entity.Export, error) {
	if e == nil {
		return nil, fmt.Errorf("schema export is nil")
	}

	exportOptions := new(apiv1.ExportOptions)
	if e.ExportOptions != "" {
		if err := protojson.Unmarshal([]byte(e.ExportOptions), exportOptions); err != nil {
			return nil, fmt.Errorf("failed to unmarshal export options: %w", err)
		}
	}

	cloudStorage := new(apiv1.CloudStorage)
	if e.CloudStorage != "" {
		if err := protojson.Unmarshal([]byte(e.CloudStorage), cloudStorage); err != nil {
			return nil, fmt.Errorf("failed to unmarshal cloud storage: %w", err)
		}
	}

	out := &apiv1.Export{
		ExportId:        e.ExportID,
		ClusterId:       e.ClusterID,
		State:           toProtoExportState(e.State),
		ExportOptions:   exportOptions,
		CloudStorage:    cloudStorage,
		Reason:          e.Reason,
		SnapshotTso:     e.SnapshotTSO,
		Username:        e.Username,
		Password:        e.Password,
		CreateTime:      timestamppb.New(e.CreatedAt),
		TidbGroupId:     e.TiDBGroupID,
		DumplingJobName: e.DumplingJobName,
	}

	if e.SnapshotTime != nil {
		out.SnapshotTime = timestamppb.New(*e.SnapshotTime)
	}
	if e.CompleteTime != nil {
		out.CompleteTime = timestamppb.New(*e.CompleteTime)
	}
	return out, nil
}

func toProtoExportState(in string) apiv1.Export_State {
	switch in {
	case schema.ExportStateCreating:
		return apiv1.Export_CREATING
	case schema.ExportStateRunning:
		return apiv1.Export_RUNNING
	case schema.ExportStateSucceeded:
		return apiv1.Export_SUCCEEDED
	case schema.ExportStateFailed:
		return apiv1.Export_FAILED
	case schema.ExportStateCanceled:
		return apiv1.Export_CANCELED
	case schema.ExportStateDeleted:
		return apiv1.Export_DELETED
	default:
		return apiv1.Export_STATE_UNSPECIFIED
	}
}

func fromProtoExportState(in apiv1.Export_State) string {
	switch in {
	case apiv1.Export_RUNNING:
		return schema.ExportStateRunning
	case apiv1.Export_SUCCEEDED:
		return schema.ExportStateSucceeded
	case apiv1.Export_FAILED:
		return schema.ExportStateFailed
	case apiv1.Export_CANCELED:
		return schema.ExportStateCanceled
	case apiv1.Export_DELETED:
		return schema.ExportStateDeleted
	default:
		return schema.ExportStateCreating
	}
}
