// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

package command

import (
	"fmt"

	"github.com/hashicorp/terraform/internal/tfdiags"
)

// errBackendLocalRead is a custom error used to alert users that state
// files on their local filesystem were not erased successfully after
// migrating that state to a remote-state backend.
type errBackendLocalRead struct {
	innerError error
}

func (e *errBackendLocalRead) Error() string {
	return fmt.Sprintf(`Error reading local state: %s

Terraform is trying to read your local state to determine if there is
state to migrate to your newly configured backend. Terraform can't continue
without this check because that would risk losing state. Please resolve the
error above and try again.`, e.innerError)
}

// errBackendMigrateLocalDelete is a custom error used to alert users that state
// files on their local filesystem were not erased successfully after migrating
// that state to a remote-state backend.
type errBackendMigrateLocalDelete struct {
	innerError error
}

func (e *errBackendMigrateLocalDelete) Error() string {
	return fmt.Sprintf(`Error deleting local state after migration: %s

Your local state is deleted after successfully migrating it to the newly
configured backend. As part of the deletion process, a backup is made at
the standard backup path unless explicitly asked not to. To cleanly operate
with a backend, we must delete the local state file. Please resolve the
issue above and retry the command.`, e.innerError)
}

// errBackendSavedUnknown is a custom error used to alert users that their
// configuration describes a backend that's not implemented in Terraform.
type errBackendNewUnknown struct {
	backendName string
}

func (e *errBackendNewUnknown) Error() string {
	return fmt.Sprintf(`The backend %q could not be found.

This is the backend specified in your Terraform configuration file.
This error could be a simple typo in your configuration, but it can also
be caused by using a Terraform version that doesn't support the specified
backend type. Please check your configuration and your Terraform version.

If you'd like to run Terraform and store state locally, you can fix this
error by removing the backend configuration from your configuration.`, e.backendName)
}

// errBackendSavedUnknown is a custom error used to alert users that their
// plan file describes a backend that's not implemented in Terraform.
type errBackendSavedUnknown struct {
	backendName string
}

func (e *errBackendSavedUnknown) Error() string {
	return fmt.Sprintf(`The backend %q could not be found.

This is the backend that this Terraform environment is configured to use
both in your configuration and saved locally as your last-used backend.
If it isn't found, it could mean an alternate version of Terraform was
used with this configuration. Please use the proper version of Terraform that
contains support for this backend.

If you'd like to force remove this backend, you must update your configuration
to not use the backend and run "terraform init" (or any other command) again.`, e.backendName)
}

// errBackendClearSaved is a custom error used to alert users that
// Terraform failed to empty the backend state file's contents.
type errBackendClearSaved struct {
	innerError error
}

func (e *errBackendClearSaved) Error() string {
	return fmt.Sprintf(`Error clearing the backend configuration: %s

Terraform removes the saved backend configuration when you're removing a
configured backend. This must be done so future Terraform runs know to not
use the backend configuration. Please look at the error above, resolve it,
and try again.`, e.innerError)
}

// errBackendInitDiag creates a diagnostic to present to users when
// users attempt to run a non-init command after making a change to their
// backend configuration.
//
// An init reason should be provided as an argument.
func errBackendInitDiag(initReason string) tfdiags.Diagnostic {
	msg := fmt.Sprintf(`Reason: %s

The "backend" is the interface that Terraform uses to store state,
perform operations, etc. If this message is showing up, it means that the
Terraform configuration you're using is using a custom configuration for
the Terraform backend.

Changes to backend configurations require reinitialization. This allows
Terraform to set up the new configuration, copy existing state, etc. Please run
"terraform init" with either the "-reconfigure" or "-migrate-state" flags to
use the current configuration.

If the change reason above is incorrect, please verify your configuration
hasn't changed and try again. At this point, no changes to your existing
configuration or state have been made.`, initReason)

	return tfdiags.Sourceless(
		tfdiags.Error,
		"Backend initialization required, please run \"terraform init\"",
		msg,
	)
}

// errStateStoreInitDiag creates a diagnostic to present to users when
// users attempt to run a non-init command after making a change to their
// state_store configuration.
//
// An init reason should be provided as an argument.
func errStateStoreInitDiag(initReason string) tfdiags.Diagnostic {
	msg := fmt.Sprintf(`Reason: %s

The "state store" is the interface that Terraform uses to store state when
performing operations on the local machine. If this message is showing up,
it means that the Terraform configuration you're using is using a custom
configuration for state storage in Terraform.

Changes to state store configurations require reinitialization. This allows
Terraform to set up the new configuration, copy existing state, etc. Please run
"terraform init" with either the "-reconfigure" or "-migrate-state" flags to
use the current configuration.

If the change reason above is incorrect, please verify your configuration
hasn't changed and try again. At this point, no changes to your existing
configuration or state have been made.`, initReason)

	return tfdiags.Sourceless(
		tfdiags.Error,
		"State store initialization required, please run \"terraform init\"",
		msg,
	)
}

// errBackendInitCloudDiag creates a diagnostic to present to users when
// an init command encounters config changes in a `cloud` block.
//
// An init reason should be provided as an argument.
func errBackendInitCloudDiag(initReason string) tfdiags.Diagnostic {
	msg := fmt.Sprintf(`Reason: %s.

Changes to the HCP Terraform configuration block require reinitialization, to discover any changes to the available workspaces.

To re-initialize, run:
  terraform init

Terraform has not yet made changes to your existing configuration or state.`, initReason)

	return tfdiags.Sourceless(
		tfdiags.Error,
		"HCP Terraform or Terraform Enterprise initialization required: please run \"terraform init\"",
		msg,
	)
}

// errBackendWriteSavedDiag creates a diagnostic to present to users when
// an init command experiences an error while writing to the backend state file.
func errBackendWriteSavedDiag(innerError error) tfdiags.Diagnostic {
	msg := fmt.Sprintf(`Error saving the backend configuration: %s

Terraform saves the complete backend configuration in a local file for
configuring the backend on future operations. This cannot be disabled. Errors
are usually due to simple file permission errors. Please look at the error
above, resolve it, and try again.`, innerError)

	return tfdiags.Sourceless(
		tfdiags.Error,
		"HCP Terraform or Terraform Enterprise initialization required: please run \"terraform init\"",
		msg,
	)
}

// errBackendNoExistingWorkspaces is returned by calling code when it expects a backend.Backend
// to report one or more workspaces exist.
//
// The returned error may be used as a sentinel error and acted upon or just wrapped in a
// diagnostic and returned.
type errBackendNoExistingWorkspaces struct{}

func (e *errBackendNoExistingWorkspaces) Error() string {
	return `No existing workspaces.

Use the "terraform workspace" command to create and select a new workspace.
If the backend already contains existing workspaces, you may need to update
the backend configuration.`
}

func errStateStoreWorkspaceCreateDiag(innerError error, storeType string) tfdiags.Diagnostic {
	msg := fmt.Sprintf(`Error creating the default workspace using pluggable state store %s: %s

This could be a bug in the provider used for state storage, or a bug in
Terraform. Please file an issue with the provider developers before reporting
a bug for Terraform.`,
		storeType,
		innerError,
	)

	return tfdiags.Sourceless(
		tfdiags.Error,
		"Cannot create the default workspace",
		msg,
	)
}

// migrateOrReconfigDiag creates a diagnostic to present to users when
// an init command encounters a mismatch in backend state and the current config
// and Terraform needs users to provide additional instructions about how Terraform
// should proceed.
var migrateOrReconfigDiag = tfdiags.Sourceless(
	tfdiags.Error,
	"Backend configuration changed",
	"A change in the backend configuration has been detected, which may require migrating existing state.\n\n"+
		"If you wish to attempt automatic migration of the state, use \"terraform init -migrate-state\".\n"+
		`If you wish to store the current configuration with no changes to the state, use "terraform init -reconfigure".`)
