/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
Kasim Baybikov, <kmbaybikov@basistech.ru>

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.

Source code: https://repository.basistech.ru/BASIS/terraform-provider-decort

Please see README.md to learn where to place source code so that it
builds seamlessly.

Documentation: https://repository.basistech.ru/BASIS/terraform-provider-decort/wiki
*/

package disks

import (
	"context"

	"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
	log "github.com/sirupsen/logrus"
	"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/disks"
	"repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants"
	"repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller"
)

func resourceDiskSnapshotCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
	c := m.(*controller.ControllerCfg)

	disk, err := utilityDiskCheckPresence(ctx, d, m)
	if err != nil {
		return diag.FromErr(err)
	}

	snapshots := disk.Snapshots
	snapshot := disks.ItemSnapshot{}
	label := d.Get("label").(string)
	for _, sn := range snapshots {
		if label == sn.Label {
			snapshot = sn
			break
		}
	}
	if label != snapshot.Label {
		return diag.Errorf("Snapshot with label \"%v\" not found", label)
	}

	if rollback := d.Get("rollback").(bool); rollback {
		req := disks.SnapshotRollbackRequest{
			DiskID:    disk.ID,
			Label:     label,
			TimeStamp: uint64(d.Get("timestamp").(int)),
		}

		log.Debugf("resourceDiskCreate: Snapshot rollback with label %s", label)
		_, err := c.CloudAPI().Disks().SnapshotRollback(ctx, req)
		if err != nil {
			return diag.FromErr(err)
		}
	}
	return resourceDiskSnapshotRead(ctx, d, m)
}

func resourceDiskSnapshotRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
	disk, err := utilityDiskCheckPresence(ctx, d, m)
	if err != nil {
		d.SetId("")
		return diag.FromErr(err)
	}

	snapshots := disk.Snapshots
	snapshot := disks.ItemSnapshot{}
	label := d.Get("label").(string)
	for _, sn := range snapshots {
		if label == sn.Label {
			snapshot = sn
			break
		}
	}
	if label != snapshot.Label {
		return diag.Errorf("Snapshot with label \"%v\" not found", label)
	}

	flattenDiskSnapshot(d, snapshot)

	return nil
}

func resourceDiskSnapshotUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
	c := m.(*controller.ControllerCfg)
	disk, err := utilityDiskCheckPresence(ctx, d, m)
	if err != nil {
		return diag.FromErr(err)
	}

	snapshots := disk.Snapshots
	snapshot := disks.ItemSnapshot{}
	label := d.Get("label").(string)
	for _, sn := range snapshots {
		if label == sn.Label {
			snapshot = sn
			break
		}
	}

	if label != snapshot.Label {
		return diag.Errorf("Snapshot with label \"%v\" not found", label)
	}

	if d.HasChange("rollback") && d.Get("rollback").(bool) == true {
		req := disks.SnapshotRollbackRequest{
			DiskID:    disk.ID,
			Label:     label,
			TimeStamp: uint64(d.Get("timestamp").(int)),
		}

		log.Debugf("resourceDiskUpdtae: Snapshot rollback with label %s", label)
		_, err := c.CloudAPI().Disks().SnapshotRollback(ctx, req)
		if err != nil {
			return diag.FromErr(err)
		}
	}

	return resourceDiskSnapshotRead(ctx, d, m)
}

func resourceDiskSnapshotDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
	c := m.(*controller.ControllerCfg)

	disk, err := utilityDiskCheckPresence(ctx, d, m)
	if err != nil {
		d.SetId("")
		return diag.FromErr(err)
	}

	req := disks.SnapshotDeleteRequest{
		DiskID: disk.ID,
		Label:  d.Get("label").(string),
	}

	_, err = c.CloudAPI().Disks().SnapshotDelete(ctx, req)
	if err != nil {
		return diag.FromErr(err)
	}

	d.SetId("")

	return nil
}

func resourceDiskSnapshotSchemaMake() map[string]*schema.Schema {
	rets := map[string]*schema.Schema{
		"disk_id": {
			Type:     schema.TypeInt,
			Required: true,
			//ForceNew:    true,
			Description: "The unique ID of the subscriber-owner of the disk",
		},
		"label": {
			Type:     schema.TypeString,
			Required: true,
			//ForceNew:    true,
			Description: "Name of the snapshot",
		},
		"rollback": {
			Type:        schema.TypeBool,
			Optional:    true,
			Default:     false,
			Description: "Needed in order to make a snapshot rollback",
		},
		"timestamp": {
			Type:        schema.TypeInt,
			Optional:    true,
			Computed:    true,
			Description: "Snapshot time",
		},
		"guid": {
			Type:        schema.TypeString,
			Computed:    true,
			Description: "ID of the snapshot",
		},
		"res_id": {
			Type:        schema.TypeString,
			Computed:    true,
			Description: "Reference to the snapshot",
		},
		"snap_set_guid": {
			Type:        schema.TypeString,
			Computed:    true,
			Description: "The set snapshot ID",
		},
		"snap_set_time": {
			Type:        schema.TypeInt,
			Computed:    true,
			Description: "The set time of the snapshot",
		},
	}
	return rets
}

func ResourceDiskSnapshot() *schema.Resource {
	return &schema.Resource{
		SchemaVersion: 1,

		CreateContext: resourceDiskSnapshotCreate,
		ReadContext:   resourceDiskSnapshotRead,
		UpdateContext: resourceDiskSnapshotUpdate,
		DeleteContext: resourceDiskSnapshotDelete,

		Importer: &schema.ResourceImporter{
			StateContext: schema.ImportStatePassthroughContext,
		},

		Timeouts: &schema.ResourceTimeout{
			Create:  &constants.Timeout600s,
			Read:    &constants.Timeout300s,
			Update:  &constants.Timeout300s,
			Delete:  &constants.Timeout300s,
			Default: &constants.Timeout300s,
		},

		Schema: resourceDiskSnapshotSchemaMake(),
	}
}
