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

package schema

import (
	"fmt"
)

// MultiLevelFieldReader reads from other field readers,
// merging their results along the way in a specific order. You can specify
// "levels" and name them in order to read only an exact level or up to
// a specific level.
//
// This is useful for saying things such as "read the field from the state
// and config and merge them" or "read the latest value of the field".
type MultiLevelFieldReader struct {
	Readers map[string]FieldReader
	Levels  []string
}

func (r *MultiLevelFieldReader) ReadField(address []string) (FieldReadResult, error) {
	return r.ReadFieldMerge(address, r.Levels[len(r.Levels)-1])
}

func (r *MultiLevelFieldReader) ReadFieldExact(
	address []string, level string) (FieldReadResult, error) {
	reader, ok := r.Readers[level]
	if !ok {
		return FieldReadResult{}, fmt.Errorf(
			"Unknown reader level: %s", level)
	}

	result, err := reader.ReadField(address)
	if err != nil {
		return FieldReadResult{}, fmt.Errorf(
			"Error reading level %s: %w", level, err)
	}

	return result, nil
}

func (r *MultiLevelFieldReader) ReadFieldMerge(
	address []string, level string) (FieldReadResult, error) {
	var result FieldReadResult
	for _, l := range r.Levels {
		if r, ok := r.Readers[l]; ok {
			out, err := r.ReadField(address)
			if err != nil {
				return FieldReadResult{}, fmt.Errorf(
					"Error reading level %s: %w", l, err)
			}

			// TODO: computed
			if out.Exists {
				result = out
			}
		}

		if l == level {
			break
		}
	}

	return result, nil
}
