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

import (
	"encoding/json"
	"errors"
	"io"
	"os"
	"strings"
)

var ErrInvalidRemotePlanFormat = errors.New("invalid remote plan format, must be 1")
var ErrInvalidRunID = errors.New("invalid run ID")
var ErrInvalidHostname = errors.New("invalid hostname")

type SavedPlanBookmark struct {
	RemotePlanFormat int    `json:"remote_plan_format"`
	RunID            string `json:"run_id"`
	Hostname         string `json:"hostname"`
}

func NewSavedPlanBookmark(runID, hostname string) SavedPlanBookmark {
	return SavedPlanBookmark{
		RemotePlanFormat: 1,
		RunID:            runID,
		Hostname:         hostname,
	}
}

func LoadSavedPlanBookmark(filepath string) (SavedPlanBookmark, error) {
	bookmark := SavedPlanBookmark{}

	file, err := os.Open(filepath)
	if err != nil {
		return bookmark, err
	}
	defer file.Close()

	data, err := io.ReadAll(file)
	if err != nil {
		return bookmark, err
	}

	err = json.Unmarshal(data, &bookmark)
	if err != nil {
		return bookmark, err
	}

	// Note that these error cases are somewhat ambiguous, but they *likely*
	// mean we're not looking at a saved plan bookmark at all. Since we're not
	// certain about the format at this point, it doesn't quite make sense to
	// emit a "known file type but bad" error struct the way we do over in the
	// planfile and statefile packages.
	if bookmark.RemotePlanFormat != 1 {
		return bookmark, ErrInvalidRemotePlanFormat
	} else if bookmark.Hostname == "" {
		return bookmark, ErrInvalidHostname
	} else if bookmark.RunID == "" || !strings.HasPrefix(bookmark.RunID, "run-") {
		return bookmark, ErrInvalidRunID
	}

	return bookmark, err
}

func (s *SavedPlanBookmark) Save(filepath string) error {
	data, _ := json.Marshal(s)

	err := os.WriteFile(filepath, data, 0644)
	if err != nil {
		return err
	}

	return nil
}
