package flattens

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/hashicorp/terraform-plugin-framework/diag"
	"github.com/hashicorp/terraform-plugin-framework/types"
	"github.com/hashicorp/terraform-plugin-log/tflog"
	"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker/disks"
	"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/client"
	"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/service/cloudbroker/disks/models"
	"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/service/cloudbroker/disks/utilities"
)

// DiskListDeletedDataSource flattens data source for disk list deleted.
// Return error in case data source is not found on the platform.
// Flatten errors are added to tflog.
func DiskListDeletedDataSource(ctx context.Context, state *models.ListDisksModel, c *client.Client) diag.Diagnostics {
	tflog.Info(ctx, "Start flattens.DiskListDeletedDataSource")

	diags := diag.Diagnostics{}

	diskList, diags := utilities.DataSourceDiskListDeletedCheckPresence(ctx, state, c)
	if diags.HasError() {
		return diags
	}

	tflog.Info(ctx, "flattens.DiskListDeletedDataSource: before flatten")

	*state = models.ListDisksModel{
		ByID:        state.ByID,
		Name:        state.Name,
		AccountName: state.AccountName,
		DiskMaxSize: state.DiskMaxSize,
		Shared:      state.Shared,
		AccountID:   state.AccountID,
		Type:        state.Type,
		SortBy:      state.SortBy,
		Page:        state.Page,
		Size:        state.Size,
		Timeouts:    state.Timeouts,

		// computed fields
		EntryCount: types.Int64Value(int64(diskList.EntryCount)),
	}

	items := make([]models.ItemDiskModel, 0, diskList.EntryCount)
	for _, recordDisk := range diskList.Data {
		diskAcl, _ := json.Marshal(recordDisk.ACL)
		d := models.ItemDiskModel{
			MachineID:           types.Int64Value(int64(recordDisk.MachineID)),
			MachineName:         types.StringValue(recordDisk.MachineName),
			AccountID:           types.Int64Value(int64(recordDisk.AccountID)),
			AccountName:         types.StringValue(recordDisk.AccountName),
			SEPType:             types.StringValue(recordDisk.SEPType),
			ACL:                 types.StringValue(string(diskAcl)),
			BootPartition:       types.Int64Value(int64(recordDisk.BootPartition)),
			Computes:            flattenComputes(ctx, recordDisk.Computes),
			CreatedTime:         types.Int64Value(int64(recordDisk.CreatedTime)),
			DeletedTime:         types.Int64Value(int64(recordDisk.DeletedTime)),
			Description:         types.StringValue(recordDisk.Description),
			DestructionTime:     types.Int64Value(int64(recordDisk.DestructionTime)),
			DiskPath:            types.StringValue(recordDisk.DiskPath),
			DeviceName:          types.StringValue(recordDisk.DeviceName),
			GID:                 types.Int64Value(int64(recordDisk.GID)),
			GUID:                types.Int64Value(int64(recordDisk.GUID)),
			ID:                  types.Int64Value(int64(recordDisk.ID)),
			ImageID:             types.Int64Value(int64(recordDisk.ImageID)),
			IQN:                 types.StringValue(recordDisk.IQN),
			Login:               types.StringValue(recordDisk.Login),
			Milestones:          types.Int64Value(int64(recordDisk.Milestones)),
			Name:                types.StringValue(recordDisk.Name),
			Order:               types.Int64Value(int64(recordDisk.Order)),
			Params:              types.StringValue(recordDisk.Params),
			ParentID:            types.Int64Value(int64(recordDisk.ParentID)),
			Password:            types.StringValue(recordDisk.Password),
			PCISlot:             types.Int64Value(int64(recordDisk.PCISlot)),
			Pool:                types.StringValue(recordDisk.Pool),
			PurgeTime:           types.Int64Value(int64(recordDisk.PurgeTime)),
			PurgeAttempts:       types.Int64Value(int64(recordDisk.PurgeAttempts)),
			RealityDeviceNumber: types.Int64Value(int64(recordDisk.RealityDeviceNumber)),
			ReferenceID:         types.StringValue(recordDisk.ReferenceID),
			ResID:               types.StringValue(recordDisk.ResID),
			ResName:             types.StringValue(recordDisk.ResName),
			Role:                types.StringValue(recordDisk.Role),
			SEPID:               types.Int64Value(int64(recordDisk.SEPID)),
			Shareable:           types.BoolValue(recordDisk.Shareable),
			SizeMax:             types.Int64Value(int64(recordDisk.SizeMax)),
			SizeUsed:            types.Float64Value(recordDisk.SizeUsed),
			Snapshots:           flattenSnapshots(ctx, recordDisk.Snapshots),
			Status:              types.StringValue(recordDisk.Status),
			TechStatus:          types.StringValue(recordDisk.TechStatus),
			Type:                types.StringValue(recordDisk.Type),
			VMID:                types.Int64Value(int64(recordDisk.VMID)),
		}

		d.Images, diags = types.ListValueFrom(ctx, types.Int64Type, recordDisk.Images)
		if diags != nil {
			tflog.Error(ctx, fmt.Sprint("flattens.DiskListDeletedDataSource: cannot flatten recordDisk.Images to d.Images", diags))
		}
		d.PresentTo, diags = types.ListValueFrom(ctx, types.Int64Type, recordDisk.PresentTo)
		if diags != nil {
			tflog.Error(ctx, fmt.Sprint("flattens.DiskListDeletedDataSource: cannot flatten recordDisk.PresentTo to d.PresentTo", diags))
		}

		iotune := models.IOTuneModel{
			ReadBytesSec:     types.Int64Value(int64(recordDisk.IOTune.ReadBytesSec)),
			ReadBytesSecMax:  types.Int64Value(int64(recordDisk.IOTune.ReadBytesSecMax)),
			ReadIOPSSec:      types.Int64Value(int64(recordDisk.IOTune.ReadIOPSSec)),
			ReadIOPSSecMax:   types.Int64Value(int64(recordDisk.IOTune.ReadIOPSSecMax)),
			SizeIOPSSec:      types.Int64Value(int64(recordDisk.IOTune.SizeIOPSSec)),
			TotalBytesSec:    types.Int64Value(int64(recordDisk.IOTune.TotalBytesSec)),
			TotalBytesSecMax: types.Int64Value(int64(recordDisk.IOTune.TotalBytesSecMax)),
			TotalIOPSSec:     types.Int64Value(int64(recordDisk.IOTune.TotalIOPSSec)),
			TotalIOPSSecMax:  types.Int64Value(int64(recordDisk.IOTune.TotalIOPSSecMax)),
			WriteBytesSec:    types.Int64Value(int64(recordDisk.IOTune.WriteBytesSec)),
			WriteBytesSecMax: types.Int64Value(int64(recordDisk.IOTune.WriteBytesSecMax)),
			WriteIOPSSec:     types.Int64Value(int64(recordDisk.IOTune.WriteIOPSSec)),
			WriteIOPSSecMax:  types.Int64Value(int64(recordDisk.IOTune.WriteIOPSSecMax)),
		}

		d.IOTune = &iotune

		repl := models.ItemReplicationModel{
			DiskID:       types.Int64Value(int64(recordDisk.Replication.DiskID)),
			PoolID:       types.StringValue(recordDisk.Replication.PoolID),
			Role:         types.StringValue(recordDisk.Replication.Role),
			SelfVolumeID: types.StringValue(recordDisk.Replication.SelfVolumeID),
			StorageID:    types.StringValue(recordDisk.Replication.StorageID),
			VolumeID:     types.StringValue(recordDisk.Replication.VolumeID),
		}

		d.Replication = &repl

		items = append(items, d)
	}
	state.Items = items

	tflog.Info(ctx, "flattens.DiskListDeletedDataSource: end flatten")
	return nil
}

func flattenComputes(ctx context.Context, computes map[string]string) []models.ComputesModel {
	tflog.Info(ctx, "Start flattenComputes")

	res := make([]models.ComputesModel, 0, len(computes))

	for k, v := range computes {
		temp := models.ComputesModel{
			ComputeID:   types.StringValue(k),
			ComputeName: types.StringValue(v),
		}

		res = append(res, temp)
	}

	tflog.Info(ctx, "End flattenComputes")
	return res
}

func flattenSnapshots(ctx context.Context, snapshots disks.ListSnapshots) []models.ItemSnapshotModel {
	tflog.Info(ctx, "Start flattenSnapshots")
	res := make([]models.ItemSnapshotModel, 0, len(snapshots))
	for _, item := range snapshots {
		temp := models.ItemSnapshotModel{
			GUID:        types.StringValue(item.GUID),
			Label:       types.StringValue(item.Label),
			ResID:       types.StringValue(item.ResID),
			SnapSetGUID: types.StringValue(item.SnapSetGUID),
			SnapSetTime: types.Int64Value(int64(item.SnapSetTime)),
			Timestamp:   types.Int64Value(int64(item.Timestamp)),
		}
		res = append(res, temp)
	}

	tflog.Info(ctx, "End flattenSnapshots")
	return res
}
