// Copyright © 2023 Cisco Systems, Inc. and its affiliates.
// All rights reserved.
//
// Licensed under the Mozilla Public 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
//
//     https://mozilla.org/MPL/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.
//
// SPDX-License-Identifier: MPL-2.0

// Code generated by "gen/generator.go"; DO NOT EDIT.

package provider

// Section below is generated&owned by "gen/generator.go". //template:begin imports
import (
	"context"
	"fmt"
	"strings"

	"github.com/CiscoDevNet/terraform-provider-iosxe/internal/provider/helpers"
	"github.com/hashicorp/terraform-plugin-framework-validators/float64validator"
	"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
	"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
	"github.com/hashicorp/terraform-plugin-framework/path"
	"github.com/hashicorp/terraform-plugin-framework/resource"
	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
	"github.com/hashicorp/terraform-plugin-framework/types"
	"github.com/hashicorp/terraform-plugin-log/tflog"
	"github.com/netascode/go-restconf"
)

// End of section. //template:end imports

// Section below is generated&owned by "gen/generator.go". //template:begin model

// Ensure provider defined types fully satisfy framework interfaces
var (
	_ resource.Resource                = &TemplateResource{}
	_ resource.ResourceWithImportState = &TemplateResource{}
)

func NewTemplateResource() resource.Resource {
	return &TemplateResource{}
}

type TemplateResource struct {
	data *IosxeProviderData
}

func (r *TemplateResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
	resp.TypeName = req.ProviderTypeName + "_template"
}

func (r *TemplateResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
	resp.Schema = schema.Schema{
		// This description is used by the documentation generator and the language server.
		MarkdownDescription: "This resource can manage the Template configuration.",

		Attributes: map[string]schema.Attribute{
			"device": schema.StringAttribute{
				MarkdownDescription: "A device name from the provider configuration.",
				Optional:            true,
			},
			"id": schema.StringAttribute{
				MarkdownDescription: "The path of the object.",
				Computed:            true,
				PlanModifiers: []planmodifier.String{
					stringplanmodifier.UseStateForUnknown(),
				},
			},
			"delete_mode": schema.StringAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Configure behavior when deleting/destroying the resource. Either delete the entire object (YANG container) being managed, or only delete the individual resource attributes configured explicitly and leave everything else as-is. Default value is `all`.").AddStringEnumDescription("all", "attributes").String,
				Optional:            true,
				Validators: []validator.String{
					stringvalidator.OneOf("all", "attributes"),
				},
			},
			"template_name": schema.StringAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Template name").String,
				Required:            true,
				PlanModifiers: []planmodifier.String{
					stringplanmodifier.RequiresReplace(),
				},
			},
			"dot1x_pae": schema.StringAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Set 802.1x interface pae type").AddStringEnumDescription("authenticator", "both", "supplicant").String,
				Optional:            true,
				Validators: []validator.String{
					stringvalidator.OneOf("authenticator", "both", "supplicant"),
				},
			},
			"dot1x_max_reauth_req": schema.Int64Attribute{
				MarkdownDescription: helpers.NewAttributeDescription("Max No. of Reauthentication Attempts").AddIntegerRangeDescription(1, 10).String,
				Optional:            true,
				Validators: []validator.Int64{
					int64validator.Between(1, 10),
				},
			},
			"dot1x_max_req": schema.Int64Attribute{
				MarkdownDescription: helpers.NewAttributeDescription("Max No. of Retries").AddIntegerRangeDescription(1, 10).String,
				Optional:            true,
				Validators: []validator.Int64{
					int64validator.Between(1, 10),
				},
			},
			"dot1x_timeout_tx_period": schema.Int64Attribute{
				MarkdownDescription: helpers.NewAttributeDescription("Timeout for supplicant retries").AddIntegerRangeDescription(1, 65535).String,
				Optional:            true,
				Validators: []validator.Int64{
					int64validator.Between(1, 65535),
				},
			},
			"service_policy_type_control_subscriber": schema.StringAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Apply a subscriber control policy to the interface").String,
				Optional:            true,
			},
			"service_policy_input": schema.StringAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("policy-map name").String,
				Optional:            true,
			},
			"service_policy_output": schema.StringAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("policy-map name").String,
				Optional:            true,
			},
			"source_template": schema.StringAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Get config from a template").String,
				Optional:            true,
			},
			"switchport_mode_trunk": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Set trunking mode to TRUNK unconditionally").String,
				Optional:            true,
			},
			"switchport_mode_access": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Set trunking mode to ACCESS unconditionally").String,
				Optional:            true,
			},
			"switchport_nonegotiate": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Device will not engage in negotiation protocol on this interface").String,
				Optional:            true,
			},
			"switchport_block_unicast": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Block unknown unicast addresses").String,
				Optional:            true,
			},
			"switchport_port_security": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Security related command").String,
				Optional:            true,
			},
			"switchport_port_security_aging_static": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Enable aging for configured secure addresses").String,
				Optional:            true,
			},
			"switchport_port_security_aging_time": schema.Int64Attribute{
				MarkdownDescription: helpers.NewAttributeDescription("Port-security aging time").AddIntegerRangeDescription(1, 1440).String,
				Optional:            true,
				Validators: []validator.Int64{
					int64validator.Between(1, 1440),
				},
			},
			"switchport_port_security_aging_type": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Port-security aging type").String,
				Optional:            true,
			},
			"switchport_port_security_aging_type_inactivity": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Aging based on inactivity time period").String,
				Optional:            true,
			},
			"switchport_port_security_maximum_range": schema.ListNestedAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("").String,
				Optional:            true,
				NestedObject: schema.NestedAttributeObject{
					Attributes: map[string]schema.Attribute{
						"range": schema.Int64Attribute{
							MarkdownDescription: helpers.NewAttributeDescription("Maximum addresses").AddIntegerRangeDescription(1, 3072).String,
							Required:            true,
							Validators: []validator.Int64{
								int64validator.Between(1, 3072),
							},
						},
						"vlan": schema.BoolAttribute{
							MarkdownDescription: helpers.NewAttributeDescription("Max secure addresses per vlan").String,
							Optional:            true,
						},
						"vlan_access": schema.BoolAttribute{
							MarkdownDescription: helpers.NewAttributeDescription("access vlan").String,
							Optional:            true,
						},
					},
				},
			},
			"switchport_port_security_violation_protect": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Security violation protect mode").String,
				Optional:            true,
			},
			"switchport_port_security_violation_restrict": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Security violation restrict mode").String,
				Optional:            true,
			},
			"switchport_port_security_violation_shutdown": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Security violation shutdown mode").String,
				Optional:            true,
			},
			"switchport_access_vlan": schema.Int64Attribute{
				MarkdownDescription: helpers.NewAttributeDescription("VLAN ID of the VLAN when this port is in access mode").AddIntegerRangeDescription(1, 4094).String,
				Optional:            true,
				Validators: []validator.Int64{
					int64validator.Between(1, 4094),
				},
			},
			"switchport_voice_vlan": schema.Int64Attribute{
				MarkdownDescription: helpers.NewAttributeDescription("Vlan for voice traffic").AddIntegerRangeDescription(1, 4094).String,
				Optional:            true,
				Validators: []validator.Int64{
					int64validator.Between(1, 4094),
				},
			},
			"switchport_private_vlan_host_association_primary_range": schema.Int64Attribute{
				MarkdownDescription: helpers.NewAttributeDescription("Primary normal range VLAN ID of the private VLAN port association").AddIntegerRangeDescription(2, 1001).String,
				Optional:            true,
				Validators: []validator.Int64{
					int64validator.Between(2, 1001),
				},
			},
			"switchport_private_vlan_host_association_secondary_range": schema.Int64Attribute{
				MarkdownDescription: helpers.NewAttributeDescription("Secondary normal range VLAN ID of the private VLAN host port association").AddIntegerRangeDescription(2, 1001).String,
				Optional:            true,
				Validators: []validator.Int64{
					int64validator.Between(2, 1001),
				},
			},
			"switchport_trunk_allowed_vlans": schema.StringAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("VLAN IDs of the allowed VLANs when this port is in trunking mode").String,
				Optional:            true,
			},
			"switchport_trunk_allowed_vlans_none": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("no VLANs").String,
				Optional:            true,
			},
			"switchport_trunk_allowed_vlans_all": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("all VLANs").String,
				Optional:            true,
			},
			"switchport_trunk_native_vlan_tag": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Set native VLAN tagging state").String,
				Optional:            true,
			},
			"switchport_trunk_native_vlan_vlan_id": schema.Int64Attribute{
				MarkdownDescription: helpers.NewAttributeDescription("VLAN ID of the native VLAN when this port is in trunking mode").AddIntegerRangeDescription(1, 4094).String,
				Optional:            true,
				Validators: []validator.Int64{
					int64validator.Between(1, 4094),
				},
			},
			"mab": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("MAC Authentication Bypass Interface Config Commands").String,
				Optional:            true,
			},
			"mab_eap": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Use EAP authentication for MAC Auth Bypass").String,
				Optional:            true,
			},
			"access_session_closed": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Enable closed access on port (disabled by default, i.e. open access)").String,
				Optional:            true,
			},
			"access_session_monitor": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Apply template to monitor access sessions on the port").String,
				Optional:            true,
			},
			"access_session_port_control": schema.StringAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Set the port-control value").AddStringEnumDescription("auto", "force-authorized", "force-unauthorized").String,
				Optional:            true,
				Validators: []validator.String{
					stringvalidator.OneOf("auto", "force-authorized", "force-unauthorized"),
				},
			},
			"access_session_control_direction": schema.StringAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Set the control-direction on the interface").AddStringEnumDescription("both", "in").String,
				Optional:            true,
				Validators: []validator.String{
					stringvalidator.OneOf("both", "in"),
				},
			},
			"access_session_host_mode": schema.StringAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Set the Host mode for authentication on this interface").AddStringEnumDescription("multi-auth", "multi-domain", "multi-host", "single-host").String,
				Optional:            true,
				Validators: []validator.String{
					stringvalidator.OneOf("multi-auth", "multi-domain", "multi-host", "single-host"),
				},
			},
			"access_session_interface_template_sticky": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Interface templates set to sticky").String,
				Optional:            true,
			},
			"access_session_interface_template_sticky_timer": schema.Int64Attribute{
				MarkdownDescription: helpers.NewAttributeDescription("Sticky timer value").AddIntegerRangeDescription(1, 65535).String,
				Optional:            true,
				Validators: []validator.Int64{
					int64validator.Between(1, 65535),
				},
			},
			"authentication_periodic": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Enable or Disable Reauthentication for this port").String,
				Optional:            true,
			},
			"authentication_timer_reauthenticate_server": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Obtain re-authentication timeout value from the server").String,
				Optional:            true,
			},
			"authentication_timer_reauthenticate_range": schema.Int64Attribute{
				MarkdownDescription: helpers.NewAttributeDescription("Enter a value between 1 and 1073741823").AddIntegerRangeDescription(1, 1073741823).String,
				Optional:            true,
				Validators: []validator.Int64{
					int64validator.Between(1, 1073741823),
				},
			},
			"spanning_tree_bpduguard_enable": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Enable BPDU guard for this interface").String,
				Optional:            true,
			},
			"spanning_tree_service_policy": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("help").String,
				Optional:            true,
			},
			"spanning_tree_portfast": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Portfast options for the interface").String,
				Optional:            true,
			},
			"spanning_tree_portfast_disable": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Disable portfast for this interface").String,
				Optional:            true,
			},
			"spanning_tree_portfast_edge": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Enable portfast edge on the interface").String,
				Optional:            true,
			},
			"spanning_tree_portfast_network": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Enable portfast network on the interface").String,
				Optional:            true,
			},
			"storm_control_broadcast_level_pps_threshold": schema.StringAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Enter threshold").String,
				Optional:            true,
			},
			"storm_control_broadcast_level_bps_threshold": schema.Float64Attribute{
				MarkdownDescription: helpers.NewAttributeDescription("Enter threshold").String,
				Optional:            true,
				Validators: []validator.Float64{
					float64validator.Between(0, 1e+11),
				},
			},
			"storm_control_broadcast_level_threshold": schema.Float64Attribute{
				MarkdownDescription: helpers.NewAttributeDescription("Enter threshold").String,
				Optional:            true,
				Validators: []validator.Float64{
					float64validator.Between(0, 10000),
				},
			},
			"storm_control_multicast_level_pps_threshold": schema.StringAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Enter threshold").String,
				Optional:            true,
			},
			"storm_control_multicast_level_bps_threshold": schema.Float64Attribute{
				MarkdownDescription: helpers.NewAttributeDescription("Enter threshold").String,
				Optional:            true,
				Validators: []validator.Float64{
					float64validator.Between(0, 1e+11),
				},
			},
			"storm_control_multicast_level_threshold": schema.Float64Attribute{
				MarkdownDescription: helpers.NewAttributeDescription("Enter threshold").String,
				Optional:            true,
				Validators: []validator.Float64{
					float64validator.Between(0, 10000),
				},
			},
			"storm_control_action_shutdown": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Shutdown this interface if a storm occurs").String,
				Optional:            true,
			},
			"storm_control_action_trap": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Send SNMP trap if a storm occurs").String,
				Optional:            true,
			},
			"load_interval": schema.Int64Attribute{
				MarkdownDescription: helpers.NewAttributeDescription("Load interval delay in seconds").AddIntegerRangeDescription(30, 600).String,
				Optional:            true,
				Validators: []validator.Int64{
					int64validator.Between(30, 600),
				},
			},
			"ip_dhcp_snooping_limit_rate": schema.Int64Attribute{
				MarkdownDescription: helpers.NewAttributeDescription("DHCP snooping rate limit").AddIntegerRangeDescription(1, 2048).String,
				Optional:            true,
				Validators: []validator.Int64{
					int64validator.Between(1, 2048),
				},
			},
			"ip_dhcp_snooping_trust": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("DHCP Snooping trust config").String,
				Optional:            true,
			},
			"ip_access_group": schema.ListNestedAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Access control list for IP packets").String,
				Optional:            true,
				NestedObject: schema.NestedAttributeObject{
					Attributes: map[string]schema.Attribute{
						"direction": schema.StringAttribute{
							MarkdownDescription: helpers.NewAttributeDescription("packet flow direction").AddStringEnumDescription("in", "out").String,
							Required:            true,
							Validators: []validator.String{
								stringvalidator.OneOf("in", "out"),
							},
						},
						"access_list": schema.StringAttribute{
							MarkdownDescription: helpers.NewAttributeDescription("Access control list name").String,
							Optional:            true,
						},
					},
				},
			},
			"subscriber_aging_inactivity_timer_value": schema.Int64Attribute{
				MarkdownDescription: helpers.NewAttributeDescription("Enter a value between 1 and 65535 in seconds").AddIntegerRangeDescription(1, 65535).String,
				Optional:            true,
				Validators: []validator.Int64{
					int64validator.Between(1, 65535),
				},
			},
			"subscriber_aging_inactivity_timer_probe": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("ARP probe").String,
				Optional:            true,
			},
			"subscriber_aging_probe": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("ARP probe").String,
				Optional:            true,
			},
			"device_tracking": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Configure device-tracking on the interface").String,
				Optional:            true,
			},
			"device_tracking_attach_policy": schema.ListNestedAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("policy name for device tracking").String,
				Optional:            true,
				NestedObject: schema.NestedAttributeObject{
					Attributes: map[string]schema.Attribute{
						"policy_name": schema.StringAttribute{
							MarkdownDescription: helpers.NewAttributeDescription("").String,
							Required:            true,
						},
						"vlan_range": schema.StringAttribute{
							MarkdownDescription: helpers.NewAttributeDescription("VLAN IDs of the VLANs for which this policy applies").String,
							Optional:            true,
						},
					},
				},
			},
			"device_tracking_vlan_range": schema.StringAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("VLAN IDs of the VLANs for which this policy applies").String,
				Optional:            true,
			},
			"cts_manual": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Supply local configuration for CTS parameters").String,
				Optional:            true,
			},
			"cts_manual_policy_static_sgt": schema.Int64Attribute{
				MarkdownDescription: helpers.NewAttributeDescription("Source Security Group Tag to apply to untagged or non-trusted incoming traffic").AddIntegerRangeDescription(2, 65519).String,
				Optional:            true,
				Validators: []validator.Int64{
					int64validator.Between(2, 65519),
				},
			},
			"cts_manual_policy_static_trusted": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Trust the Source Group Tags (SGT) that the peer uses for sending").String,
				Optional:            true,
			},
			"cts_manual_propagate_sgt": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("CTS SGT Propagation configuration").String,
				Optional:            true,
			},
			"cts_role_based_enforcement": schema.BoolAttribute{
				MarkdownDescription: helpers.NewAttributeDescription("Enable Role-based Access Control enforcement").String,
				Optional:            true,
			},
		},
	}
}

func (r *TemplateResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) {
	if req.ProviderData == nil {
		return
	}

	r.data = req.ProviderData.(*IosxeProviderData)
}

// End of section. //template:end model

// Section below is generated&owned by "gen/generator.go". //template:begin create

func (r *TemplateResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
	var plan Template

	// Read plan
	diags := req.Plan.Get(ctx, &plan)
	resp.Diagnostics.Append(diags...)
	if resp.Diagnostics.HasError() {
		return
	}

	tflog.Debug(ctx, fmt.Sprintf("%s: Beginning Create", plan.getPath()))

	device, ok := r.data.Devices[plan.Device.ValueString()]
	if !ok {
		resp.Diagnostics.AddAttributeError(path.Root("device"), "Invalid device", fmt.Sprintf("Device '%s' does not exist in provider configuration.", plan.Device.ValueString()))
		return
	}

	if device.Managed {
		// Create object
		body := plan.toBody(ctx)

		emptyLeafsDelete := plan.getEmptyLeafsDelete(ctx)
		tflog.Debug(ctx, fmt.Sprintf("List of empty leafs to delete: %+v", emptyLeafsDelete))

		if YangPatch {
			edits := []restconf.YangPatchEdit{restconf.NewYangPatchEdit("merge", plan.getPath(), restconf.Body{Str: body})}
			for _, i := range emptyLeafsDelete {
				edits = append(edits, restconf.NewYangPatchEdit("remove", i, restconf.Body{}))
			}
			_, err := device.Client.YangPatchData("", "1", "", edits)
			if err != nil {
				resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to configure object, got error: %s", err))
				return
			}
		} else {
			res, err := device.Client.PatchData(plan.getPathShort(), body)
			if len(res.Errors.Error) > 0 && res.Errors.Error[0].ErrorMessage == "patch to a nonexistent resource" {
				_, err = device.Client.PutData(plan.getPath(), body)
			}
			if err != nil {
				resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to configure object (PATCH, %s), got error: %s", plan.getPathShort(), err))
				return
			}
			for _, i := range emptyLeafsDelete {
				res, err := device.Client.DeleteData(i)
				if err != nil && res.StatusCode != 404 {
					resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to delete object (%s), got error: %s", i, err))
					return
				}
			}
		}
	}

	plan.Id = types.StringValue(plan.getPath())

	tflog.Debug(ctx, fmt.Sprintf("%s: Create finished successfully", plan.getPath()))

	diags = resp.State.Set(ctx, &plan)
	resp.Diagnostics.Append(diags...)

	helpers.SetFlagImporting(ctx, false, resp.Private, &resp.Diagnostics)
}

// End of section. //template:end create

// Section below is generated&owned by "gen/generator.go". //template:begin read

func (r *TemplateResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
	var state Template

	// Read state
	diags := req.State.Get(ctx, &state)
	resp.Diagnostics.Append(diags...)
	if resp.Diagnostics.HasError() {
		return
	}

	tflog.Debug(ctx, fmt.Sprintf("%s: Beginning Read", state.Id.ValueString()))

	device, ok := r.data.Devices[state.Device.ValueString()]
	if !ok {
		resp.Diagnostics.AddAttributeError(path.Root("device"), "Invalid device", fmt.Sprintf("Device '%s' does not exist in provider configuration.", state.Device.ValueString()))
		return
	}

	if device.Managed {
		res, err := device.Client.GetData(state.Id.ValueString())
		if res.StatusCode == 404 {
			state = Template{Device: state.Device, Id: state.Id}
		} else {
			if err != nil {
				resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to retrieve object (%s), got error: %s", state.Id.ValueString(), err))
				return
			}

			imp, diags := helpers.IsFlagImporting(ctx, req)
			if resp.Diagnostics.Append(diags...); resp.Diagnostics.HasError() {
				return
			}

			// After `terraform import` we switch to a full read.
			if imp {
				state.fromBody(ctx, res.Res)
			} else {
				state.updateFromBody(ctx, res.Res)
			}
		}
	}

	tflog.Debug(ctx, fmt.Sprintf("%s: Read finished successfully", state.Id.ValueString()))

	diags = resp.State.Set(ctx, &state)
	resp.Diagnostics.Append(diags...)

	helpers.SetFlagImporting(ctx, false, resp.Private, &resp.Diagnostics)
}

// End of section. //template:end read

// Section below is generated&owned by "gen/generator.go". //template:begin update

func (r *TemplateResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
	var plan, state Template

	// Read plan
	diags := req.Plan.Get(ctx, &plan)
	resp.Diagnostics.Append(diags...)
	if resp.Diagnostics.HasError() {
		return
	}

	// Read state
	diags = req.State.Get(ctx, &state)
	resp.Diagnostics.Append(diags...)
	if resp.Diagnostics.HasError() {
		return
	}

	tflog.Debug(ctx, fmt.Sprintf("%s: Beginning Update", plan.Id.ValueString()))

	device, ok := r.data.Devices[plan.Device.ValueString()]
	if !ok {
		resp.Diagnostics.AddAttributeError(path.Root("device"), "Invalid device", fmt.Sprintf("Device '%s' does not exist in provider configuration.", plan.Device.ValueString()))
		return
	}

	if device.Managed {
		body := plan.toBody(ctx)

		deletedItems := plan.getDeletedItems(ctx, state)
		tflog.Debug(ctx, fmt.Sprintf("Removed items to delete: %+v", deletedItems))

		emptyLeafsDelete := plan.getEmptyLeafsDelete(ctx)
		tflog.Debug(ctx, fmt.Sprintf("List of empty leafs to delete: %+v", emptyLeafsDelete))

		if YangPatch {
			var edits []restconf.YangPatchEdit
			for _, i := range deletedItems {
				edits = append(edits, restconf.NewYangPatchEdit("remove", i, restconf.Body{}))
			}
			edits = append(edits, restconf.NewYangPatchEdit("merge", plan.getPath(), restconf.Body{Str: body}))
			for _, i := range emptyLeafsDelete {
				edits = append(edits, restconf.NewYangPatchEdit("remove", i, restconf.Body{}))
			}
			_, err := device.Client.YangPatchData("", "1", "", edits)
			if err != nil {
				resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to update object, got error: %s", err))
				return
			}
		} else {
			for _, i := range deletedItems {
				res, err := device.Client.DeleteData(i)
				if err != nil && res.StatusCode != 404 {
					resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to delete object (%s), got error: %s", i, err))
					return
				}
			}
			res, err := device.Client.PatchData(plan.getPathShort(), body)
			if len(res.Errors.Error) > 0 && res.Errors.Error[0].ErrorMessage == "patch to a nonexistent resource" {
				_, err = device.Client.PutData(plan.getPath(), body)
			}
			if err != nil {
				resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to configure object (PATCH, %s), got error: %s", plan.getPathShort(), err))
				return
			}
			for _, i := range emptyLeafsDelete {
				res, err := device.Client.DeleteData(i)
				if err != nil && res.StatusCode != 404 {
					resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to delete object (%s), got error: %s", i, err))
					return
				}
			}
		}
	}

	tflog.Debug(ctx, fmt.Sprintf("%s: Update finished successfully", plan.Id.ValueString()))

	diags = resp.State.Set(ctx, &plan)
	resp.Diagnostics.Append(diags...)
}

// End of section. //template:end update

// Section below is generated&owned by "gen/generator.go". //template:begin delete

func (r *TemplateResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
	var state Template

	// Read state
	diags := req.State.Get(ctx, &state)
	resp.Diagnostics.Append(diags...)
	if resp.Diagnostics.HasError() {
		return
	}

	tflog.Debug(ctx, fmt.Sprintf("%s: Beginning Delete", state.Id.ValueString()))

	device, ok := r.data.Devices[state.Device.ValueString()]
	if !ok {
		resp.Diagnostics.AddAttributeError(path.Root("device"), "Invalid device", fmt.Sprintf("Device '%s' does not exist in provider configuration.", state.Device.ValueString()))
		return
	}

	if device.Managed {
		deleteMode := "all"
		if state.DeleteMode.ValueString() == "all" {
			deleteMode = "all"
		} else if state.DeleteMode.ValueString() == "attributes" {
			deleteMode = "attributes"
		}

		if deleteMode == "all" {
			res, err := device.Client.DeleteData(state.Id.ValueString())
			if err != nil && res.StatusCode != 404 && res.StatusCode != 400 {
				resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to delete object (%s), got error: %s", state.Id.ValueString(), err))
				return
			}
		} else {
			deletePaths := state.getDeletePaths(ctx)
			tflog.Debug(ctx, fmt.Sprintf("Paths to delete: %+v", deletePaths))

			if YangPatch {
				edits := []restconf.YangPatchEdit{}
				for _, i := range deletePaths {
					edits = append(edits, restconf.NewYangPatchEdit("remove", i, restconf.Body{}))
				}
				_, err := device.Client.YangPatchData("", "1", "", edits)
				if err != nil {
					resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to delete object, got error: %s", err))
					return
				}
			} else {
				for _, i := range deletePaths {
					res, err := device.Client.DeleteData(i)
					if err != nil && res.StatusCode != 404 {
						resp.Diagnostics.AddWarning("Client Warning", fmt.Sprintf("Failed to delete object (%s), got error: %s", i, err))
					}
				}
			}
		}
	}

	tflog.Debug(ctx, fmt.Sprintf("%s: Delete finished successfully", state.Id.ValueString()))

	resp.State.RemoveResource(ctx)
}

// End of section. //template:end delete

// Section below is generated&owned by "gen/generator.go". //template:begin import

func (r *TemplateResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
	idParts := strings.Split(req.ID, ",")
	idParts = helpers.RemoveEmptyStrings(idParts)

	if len(idParts) != 1 && len(idParts) != 2 {
		expectedIdentifier := "Expected import identifier with format: '<template_name>'"
		expectedIdentifier += " or '<template_name>,<device>'"
		resp.Diagnostics.AddError(
			"Unexpected Import Identifier",
			fmt.Sprintf("%s. Got: %q", expectedIdentifier, req.ID),
		)
		return
	}
	resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("template_name"), idParts[0])...)
	if len(idParts) == 2 {
		resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("device"), idParts[len(idParts)-1])...)
	}

	// construct path for 'id' attribute
	var state Template
	diags := resp.State.Get(ctx, &state)
	resp.Diagnostics.Append(diags...)
	if resp.Diagnostics.HasError() {
		return
	}
	resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("id"), state.getPath())...)

	helpers.SetFlagImporting(ctx, true, resp.Private, &resp.Diagnostics)
}

// End of section. //template:end import
