// 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 provider
import (
	"context"
	"os"
	"slices"
	"strconv"
	"strings"

	"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
	"github.com/hashicorp/terraform-plugin-framework/datasource"
	"github.com/hashicorp/terraform-plugin-framework/provider"
	"github.com/hashicorp/terraform-plugin-framework/provider/schema"
	"github.com/hashicorp/terraform-plugin-framework/resource"
	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
	"github.com/hashicorp/terraform-plugin-framework/types"
	"github.com/netascode/go-restconf"
)

const (
	YangPatch = false
)

// IosxeProvider defines the provider implementation.
type IosxeProvider struct {
	// version is set to the provider version on release, "dev" when the
	// provider is built and ran locally, and "test" when running acceptance
	// testing.
	version string
}

// IosxeProviderModel describes the provider data model.
type IosxeProviderModel struct {
	Username           types.String               `tfsdk:"username"`
	Password           types.String               `tfsdk:"password"`
	URL                types.String               `tfsdk:"url"`
	Insecure           types.Bool                 `tfsdk:"insecure"`
	Retries            types.Int64                `tfsdk:"retries"`
	LockReleaseTimeout types.Int64                `tfsdk:"lock_release_timeout"`
	SelectedDevices    types.List                 `tfsdk:"selected_devices"`
	Devices            []IosxeProviderModelDevice `tfsdk:"devices"`
}

type IosxeProviderModelDevice struct {
	Name    types.String `tfsdk:"name"`
	URL     types.String `tfsdk:"url"`
	Managed types.Bool   `tfsdk:"managed"`
}

// IosxeProviderData describes the data maintained by the provider.
type IosxeProviderData struct {
	Devices map[string]*IosxeProviderDataDevice
}

type IosxeProviderDataDevice struct {
	Client  *restconf.Client
	Managed bool
}

func (p *IosxeProvider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) {
	resp.TypeName = "iosxe"
	resp.Version = p.version
}

func (p *IosxeProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) {
	resp.Schema = schema.Schema{
		Attributes: map[string]schema.Attribute{
			"username": schema.StringAttribute{
				MarkdownDescription: "Username for the IOS-XE device. This can also be set as the IOSXE_USERNAME environment variable.",
				Optional:            true,
			},
			"password": schema.StringAttribute{
				MarkdownDescription: "Password for the IOS-XE device. This can also be set as the IOSXE_PASSWORD environment variable.",
				Optional:            true,
				Sensitive:           true,
			},
			"url": schema.StringAttribute{
				MarkdownDescription: "URL of the Cisco IOS-XE device. Optionally a port can be added with `:12345`. The default port is `443`. This can also be set as the IOSXE_URL environment variable.",
				Optional:            true,
			},
			"insecure": schema.BoolAttribute{
				MarkdownDescription: "Allow insecure HTTPS client. This can also be set as the IOSXE_INSECURE environment variable. Defaults to `true`.",
				Optional:            true,
			},
			"retries": schema.Int64Attribute{
				MarkdownDescription: "Number of retries for REST API calls. This can also be set as the IOSXE_RETRIES environment variable. Defaults to `10`.",
				Optional:            true,
				Validators: []validator.Int64{
					int64validator.Between(0, 99),
				},
			},
			"lock_release_timeout": schema.Int64Attribute{
				MarkdownDescription: "Number of seconds to wait for the device database lock to be released. This can also be set as the IOSXE_LOCK_RELEASE_TIMEOUT environment variable. Defaults to `120`.",
				Optional:            true,
				Validators: []validator.Int64{
					int64validator.Between(0, 600),
				},
			},
			"selected_devices": schema.ListAttribute{
				MarkdownDescription: "This can be used to select a list of devices to manage from the `devices` list. Selected devices will be managed while other devices will be skipped and their state will be frozen. This can be used to deploy changes to a subset of devices. Defaults to all devices.",
				Optional:            true,
				ElementType:         types.StringType,
			},
			"devices": schema.ListNestedAttribute{
				MarkdownDescription: "This can be used to manage a list of devices from a single provider. All devices must use the same credentials. Each resource and data source has an optional attribute named `device`, which can then select a device by its name from this list.",
				Optional:            true,
				NestedObject: schema.NestedAttributeObject{
					Attributes: map[string]schema.Attribute{
						"name": schema.StringAttribute{
							MarkdownDescription: "Device name.",
							Required:            true,
						},
						"url": schema.StringAttribute{
							MarkdownDescription: "URL of the Cisco IOS-XE device.",
							Required:            true,
						},
						"managed": schema.BoolAttribute{
							MarkdownDescription: "Enable or disable device management. This can be used to temporarily skip a device due to maintainance for example. Defaults to `true`.",
							Optional:            true,
						},
					},
				},
			},
		},
	}
}

func (p *IosxeProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) {
	// Retrieve provider data from configuration
	var config IosxeProviderModel
	diags := req.Config.Get(ctx, &config)
	resp.Diagnostics.Append(diags...)
	if resp.Diagnostics.HasError() {
		return
	}

	// User must provide a username to the provider
	var username string
	if config.Username.IsUnknown() {
		// Cannot connect to client with an unknown value
		resp.Diagnostics.AddWarning(
			"Unable to create client",
			"Cannot use unknown value as username",
		)
		return
	}

	if config.Username.IsNull() {
		username = os.Getenv("IOSXE_USERNAME")
	} else {
		username = config.Username.ValueString()
	}

	if username == "" {
		// Error vs warning - empty value must stop execution
		resp.Diagnostics.AddError(
			"Unable to find username",
			"Username cannot be an empty string",
		)
		return
	}

	// User must provide a password to the provider
	var password string
	if config.Password.IsUnknown() {
		// Cannot connect to client with an unknown value
		resp.Diagnostics.AddWarning(
			"Unable to create client",
			"Cannot use unknown value as password",
		)
		return
	}

	if config.Password.IsNull() {
		password = os.Getenv("IOSXE_PASSWORD")
	} else {
		password = config.Password.ValueString()
	}

	if password == "" {
		// Error vs warning - empty value must stop execution
		resp.Diagnostics.AddError(
			"Unable to find password",
			"Password cannot be an empty string",
		)
		return
	}

	// User must provide a username to the provider
	var url string
	if config.URL.IsUnknown() {
		// Cannot connect to client with an unknown value
		resp.Diagnostics.AddWarning(
			"Unable to create client",
			"Cannot use unknown value as url",
		)
		return
	}

	if config.URL.IsNull() {
		url = os.Getenv("IOSXE_URL")
		if url == "" && len(config.Devices) > 0 {
			url = config.Devices[0].URL.ValueString()
		}
	} else {
		url = config.URL.ValueString()
	}

	if url == "" {
		// Error vs warning - empty value must stop execution
		resp.Diagnostics.AddError(
			"Unable to find url",
			"URL cannot be an empty string",
		)
		return
	}

	var insecure bool
	if config.Insecure.IsUnknown() {
		// Cannot connect to client with an unknown value
		resp.Diagnostics.AddWarning(
			"Unable to create client",
			"Cannot use unknown value as insecure",
		)
		return
	}

	if config.Insecure.IsNull() {
		insecureStr := os.Getenv("IOSXE_INSECURE")
		if insecureStr == "" {
			insecure = true
		} else {
			insecure, _ = strconv.ParseBool(insecureStr)
		}
	} else {
		insecure = config.Insecure.ValueBool()
	}

	var retries int64
	if config.Retries.IsUnknown() {
		// Cannot connect to client with an unknown value
		resp.Diagnostics.AddWarning(
			"Unable to create client",
			"Cannot use unknown value as retries",
		)
		return
	}

	if config.Retries.IsNull() {
		retriesStr := os.Getenv("IOSXE_RETRIES")
		if retriesStr == "" {
			retries = 10
		} else {
			retries, _ = strconv.ParseInt(retriesStr, 0, 64)
		}
	} else {
		retries = config.Retries.ValueInt64()
	}

	var lockReleaseTimeout int64
	if config.LockReleaseTimeout.IsUnknown() {
		// Cannot connect to client with an unknown value
		resp.Diagnostics.AddWarning(
			"Unable to create client",
			"Cannot use unknown value as lockReleaseTimeout",
		)
		return
	}

	if config.LockReleaseTimeout.IsNull() {
		lockReleaseTimeoutStr := os.Getenv("IOSXE_LOCK_RELEASE_TIMEOUT")
		if lockReleaseTimeoutStr == "" {
			lockReleaseTimeout = 120
		} else {
			lockReleaseTimeout, _ = strconv.ParseInt(lockReleaseTimeoutStr, 0, 64)
		}
	} else {
		lockReleaseTimeout = config.LockReleaseTimeout.ValueInt64()
	}

	var selectedDevices []string
	if config.SelectedDevices.IsUnknown() {
		// Cannot connect to client with an unknown value
		resp.Diagnostics.AddWarning(
			"Unable to create client",
			"Cannot use unknown value as selectedDevices",
		)
		return
	}

	if config.SelectedDevices.IsNull() {
		selectedDevicesStr := os.Getenv("IOSXE_SELECTED_DEVICES")
		if selectedDevicesStr != "" {
			selectedDevices = strings.Split(selectedDevicesStr, ",")
		}
	} else {
		diags = config.SelectedDevices.ElementsAs(ctx, &selectedDevices, false)
		resp.Diagnostics.Append(diags...)
	}

	data := IosxeProviderData{}
	data.Devices = make(map[string]*IosxeProviderDataDevice)

	c, err := restconf.NewClient(url, username, password, insecure, restconf.MaxRetries(int(retries)), restconf.SkipDiscovery("/restconf", true), restconf.LockReleaseTimeout(int(lockReleaseTimeout)))
	if err != nil {
		resp.Diagnostics.AddError(
			"Unable to create client",
			"Unable to create restconf client:\n\n"+err.Error(),
		)
		return
	}
	data.Devices[""] = &IosxeProviderDataDevice{Client: c, Managed: true}

	for _, device := range config.Devices {
		var managed bool
		if len(selectedDevices) > 0 {
			if slices.Contains(selectedDevices, device.Name.ValueString()) {
				managed = true
			} else {
				managed = false
			}
		} else {
			if device.Managed.IsUnknown() || device.Managed.IsNull() {
				managed = true
			} else {
				managed = device.Managed.ValueBool()
			}
		}
		c, err := restconf.NewClient(device.URL.ValueString(), username, password, insecure, restconf.MaxRetries(int(retries)), restconf.SkipDiscovery("/restconf", true), restconf.LockReleaseTimeout(int(lockReleaseTimeout)))
		if err != nil {
			resp.Diagnostics.AddError(
				"Unable to create client",
				"Unable to create restconf client:\n\n"+err.Error(),
			)
			return
		}
		data.Devices[device.Name.ValueString()] = &IosxeProviderDataDevice{Client: c, Managed: managed}
	}

	resp.DataSourceData = &data
	resp.ResourceData = &data
}

func (p *IosxeProvider) Resources(ctx context.Context) []func() resource.Resource {
	return []func() resource.Resource{
		NewRestconfResource,
		NewSaveConfigResource,
		NewCliResource,
		NewAAAResource,
		NewAAAAccountingResource,
		NewAAAAuthenticationResource,
		NewAAAAuthorizationResource,
		NewAccessListExtendedResource,
		NewAccessListRoleBasedResource,
		NewAccessListStandardResource,
		NewARPResource,
		NewASPathAccessListResource,
		NewBannerResource,
		NewBFDResource,
		NewBFDTemplateMultiHopResource,
		NewBFDTemplateSingleHopResource,
		NewBGPResource,
		NewBGPAddressFamilyIPv4Resource,
		NewBGPAddressFamilyIPv4VRFResource,
		NewBGPAddressFamilyIPv6Resource,
		NewBGPAddressFamilyIPv6VRFResource,
		NewBGPAddressFamilyL2VPNResource,
		NewBGPIPv4UnicastNeighborResource,
		NewBGPIPv4UnicastVRFNeighborResource,
		NewBGPIPv6UnicastNeighborResource,
		NewBGPL2VPNEVPNNeighborResource,
		NewBGPNeighborResource,
		NewCDPResource,
		NewClassMapResource,
		NewClockResource,
		NewCommunityListExpandedResource,
		NewCommunityListStandardResource,
		NewCryptoIKEv2Resource,
		NewCryptoIKEv2KeyringResource,
		NewCryptoIKEv2PolicyResource,
		NewCryptoIKEv2ProfileResource,
		NewCryptoIKEv2ProposalResource,
		NewCryptoIPSecProfileResource,
		NewCryptoIPSecTransformSetResource,
		NewCryptoPKIResource,
		NewCTSResource,
		NewDeviceSensorResource,
		NewDHCPResource,
		NewDot1xResource,
		NewEEMResource,
		NewErrdisableResource,
		NewEVPNResource,
		NewEVPNInstanceResource,
		NewFlowExporterResource,
		NewFlowMonitorResource,
		NewFlowRecordResource,
		NewInterfaceEthernetResource,
		NewInterfaceLoopbackResource,
		NewInterfaceMPLSResource,
		NewInterfaceNVEResource,
		NewInterfaceOSPFResource,
		NewInterfaceOSPFv3Resource,
		NewInterfacePIMResource,
		NewInterfacePortChannelResource,
		NewInterfacePortChannelSubinterfaceResource,
		NewInterfaceSwitchportResource,
		NewInterfaceTunnelResource,
		NewInterfaceVLANResource,
		NewLicenseResource,
		NewLineResource,
		NewLLDPResource,
		NewLoggingResource,
		NewMDTSubscriptionResource,
		NewMSDPResource,
		NewNATResource,
		NewNTPResource,
		NewOSPFResource,
		NewOSPFVRFResource,
		NewPIMResource,
		NewPlatformResource,
		NewPolicyMapResource,
		NewPolicyMapEventResource,
		NewPrefixListResource,
		NewRadiusResource,
		NewRadiusServerResource,
		NewRouteMapResource,
		NewServiceResource,
		NewServiceTemplateResource,
		NewSLAResource,
		NewSNMPServerResource,
		NewSpanningTreeResource,
		NewStaticRouteResource,
		NewStaticRoutesVRFResource,
		NewSystemResource,
		NewTACACSServerResource,
		NewTemplateResource,
		NewUDLDResource,
		NewUsernameResource,
		NewVLANResource,
		NewVLANAccessMapResource,
		NewVLANConfigurationResource,
		NewVLANFilterResource,
		NewVLANGroupResource,
		NewVRFResource,
		NewVTPResource,
	}
}

func (p *IosxeProvider) DataSources(ctx context.Context) []func() datasource.DataSource {
	return []func() datasource.DataSource{
		NewRestconfDataSource,
		NewAAADataSource,
		NewAAAAccountingDataSource,
		NewAAAAuthenticationDataSource,
		NewAAAAuthorizationDataSource,
		NewAccessListExtendedDataSource,
		NewAccessListRoleBasedDataSource,
		NewAccessListStandardDataSource,
		NewARPDataSource,
		NewASPathAccessListDataSource,
		NewBannerDataSource,
		NewBFDDataSource,
		NewBFDTemplateMultiHopDataSource,
		NewBFDTemplateSingleHopDataSource,
		NewBGPDataSource,
		NewBGPAddressFamilyIPv4DataSource,
		NewBGPAddressFamilyIPv4VRFDataSource,
		NewBGPAddressFamilyIPv6DataSource,
		NewBGPAddressFamilyIPv6VRFDataSource,
		NewBGPAddressFamilyL2VPNDataSource,
		NewBGPIPv4UnicastNeighborDataSource,
		NewBGPIPv4UnicastVRFNeighborDataSource,
		NewBGPIPv6UnicastNeighborDataSource,
		NewBGPL2VPNEVPNNeighborDataSource,
		NewBGPNeighborDataSource,
		NewCDPDataSource,
		NewClassMapDataSource,
		NewClockDataSource,
		NewCommunityListExpandedDataSource,
		NewCommunityListStandardDataSource,
		NewCryptoIKEv2DataSource,
		NewCryptoIKEv2KeyringDataSource,
		NewCryptoIKEv2PolicyDataSource,
		NewCryptoIKEv2ProfileDataSource,
		NewCryptoIKEv2ProposalDataSource,
		NewCryptoIPSecProfileDataSource,
		NewCryptoIPSecTransformSetDataSource,
		NewCryptoPKIDataSource,
		NewCTSDataSource,
		NewDeviceSensorDataSource,
		NewDHCPDataSource,
		NewDot1xDataSource,
		NewEEMDataSource,
		NewErrdisableDataSource,
		NewEVPNDataSource,
		NewEVPNInstanceDataSource,
		NewFlowExporterDataSource,
		NewFlowMonitorDataSource,
		NewFlowRecordDataSource,
		NewInterfaceEthernetDataSource,
		NewInterfaceLoopbackDataSource,
		NewInterfaceMPLSDataSource,
		NewInterfaceNVEDataSource,
		NewInterfaceOSPFDataSource,
		NewInterfaceOSPFv3DataSource,
		NewInterfacePIMDataSource,
		NewInterfacePortChannelDataSource,
		NewInterfacePortChannelSubinterfaceDataSource,
		NewInterfaceSwitchportDataSource,
		NewInterfaceTunnelDataSource,
		NewInterfaceVLANDataSource,
		NewLicenseDataSource,
		NewLineDataSource,
		NewLLDPDataSource,
		NewLoggingDataSource,
		NewMDTSubscriptionDataSource,
		NewMSDPDataSource,
		NewNATDataSource,
		NewNTPDataSource,
		NewOSPFDataSource,
		NewOSPFVRFDataSource,
		NewPIMDataSource,
		NewPlatformDataSource,
		NewPolicyMapDataSource,
		NewPolicyMapEventDataSource,
		NewPrefixListDataSource,
		NewRadiusDataSource,
		NewRadiusServerDataSource,
		NewRouteMapDataSource,
		NewServiceDataSource,
		NewServiceTemplateDataSource,
		NewSLADataSource,
		NewSNMPServerDataSource,
		NewSpanningTreeDataSource,
		NewStaticRouteDataSource,
		NewStaticRoutesVRFDataSource,
		NewSystemDataSource,
		NewTACACSServerDataSource,
		NewTemplateDataSource,
		NewUDLDDataSource,
		NewUsernameDataSource,
		NewVLANDataSource,
		NewVLANAccessMapDataSource,
		NewVLANConfigurationDataSource,
		NewVLANFilterDataSource,
		NewVLANGroupDataSource,
		NewVRFDataSource,
		NewVTPDataSource,
	}
}

func New(version string) func() provider.Provider {
	return func() provider.Provider {
		return &IosxeProvider{
			version: version,
		}
	}
}

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