// Copyright (c) The OpenTofu Authors
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2023 HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package cloud

import (
	"context"
	"fmt"
	"strconv"
	"time"

	"github.com/hashicorp/go-tfe"
	"github.com/mitchellh/cli"
	"github.com/opentofu/opentofu/internal/backend"
)

// IntegrationOutputWriter is an interface used to to write output tailored for
// Terraform Cloud integrations
type IntegrationOutputWriter interface {
	End()
	OutputElapsed(message string, maxMessage int)
	Output(str string)
	SubOutput(str string)
}

// IntegrationContext is a set of data that is useful when performing Terraform Cloud integration operations
type IntegrationContext struct {
	B             *Cloud
	StopContext   context.Context
	CancelContext context.Context
	Op            *backend.Operation
	Run           *tfe.Run
}

// integrationCLIOutput implements IntegrationOutputWriter
type integrationCLIOutput struct {
	CLI       cli.Ui
	Colorizer Colorer
	started   time.Time
}

var _ IntegrationOutputWriter = (*integrationCLIOutput)(nil) // Compile time check

func (s *IntegrationContext) Poll(backoffMinInterval float64, backoffMaxInterval float64, every func(i int) (bool, error)) error {
	for i := 0; ; i++ {
		select {
		case <-s.StopContext.Done():
			return s.StopContext.Err()
		case <-s.CancelContext.Done():
			return s.CancelContext.Err()
		case <-time.After(backoff(backoffMinInterval, backoffMaxInterval, i)):
			// blocks for a time between min and max
		}

		cont, err := every(i)
		if !cont {
			return err
		}
	}
}

// BeginOutput writes a preamble to the CLI and creates a new IntegrationOutputWriter interface
// to write the remaining CLI output to. Use IntegrationOutputWriter.End() to complete integration
// output
func (s *IntegrationContext) BeginOutput(name string) IntegrationOutputWriter {
	var result IntegrationOutputWriter = &integrationCLIOutput{
		CLI:       s.B.CLI,
		Colorizer: s.B.Colorize(),
		started:   time.Now(),
	}

	result.Output("\n[bold]" + name + ":\n")

	return result
}

// End writes the termination output for the integration
func (s *integrationCLIOutput) End() {
	if s.CLI == nil {
		return
	}

	s.CLI.Output("\n------------------------------------------------------------------------\n")
}

// Output writes a string after colorizing it using any [colorstrings](https://github.com/mitchellh/colorstring) it contains
func (s *integrationCLIOutput) Output(str string) {
	if s.CLI == nil {
		return
	}
	s.CLI.Output(s.Colorizer.Color(str))
}

// SubOutput writes a string prefixed by a "│ " after colorizing it using any [colorstrings](https://github.com/mitchellh/colorstring) it contains
func (s *integrationCLIOutput) SubOutput(str string) {
	if s.CLI == nil {
		return
	}
	s.CLI.Output(s.Colorizer.Color(fmt.Sprintf("[reset]│ %s", str)))
}

// OutputElapsed writes a string followed by the amount of time that has elapsed since calling BeginOutput.
// Example pending output; the variable spacing (50 chars) allows up to 99 tasks (two digits) in each category:
// ---------------
// 13 tasks still pending, 0 passed, 0 failed ...
// 13 tasks still pending, 0 passed, 0 failed ...       (8s elapsed)
// 13 tasks still pending, 0 passed, 0 failed ...       (19s elapsed)
// 13 tasks still pending, 0 passed, 0 failed ...       (33s elapsed)
func (s *integrationCLIOutput) OutputElapsed(message string, maxMessage int) {
	if s.CLI == nil {
		return
	}
	elapsed := time.Since(s.started).Truncate(1 * time.Second)
	s.CLI.Output(fmt.Sprintf("%-"+strconv.FormatInt(int64(maxMessage), 10)+"s", message) + s.Colorizer.Color(fmt.Sprintf("[dim](%s elapsed)", elapsed)))
}
