package validation

import (
	"encoding/json"
	"fmt"
	"strings"

	"github.com/ethereum-optimism/superchain-registry/validation/standard"
)

// ImmutableReference to store the start/length of a contract's immutable references. Immutables
// are directly assigned values once or are initialized in a constructor. They are encoded in the contract bytecode,
// and the AST that is generated by the compiler has info about the offset & length within the bytecode.
type ImmutableReference struct {
	Start  int `json:"start"`
	Length int `json:"length"`
}

// Define a struct to represent the structure of the JSON data
type BytecodeAndImmutableReferences struct {
	Bytecode            []byte                          `json:"object"`
	ImmutableReferences map[string][]ImmutableReference `json:"immutableReferences"`
}

// initBytecodeImmutableMask returns the struct with coordinates of the immutable references in the deployed bytecode, if present
func initBytecodeImmutableMask(bytecode []byte, tag standard.Tag, contractName string) (*BytecodeAndImmutableReferences, error) {
	parsedImmutables := map[string][]ImmutableReference{}
	proxyContract := strings.HasSuffix(contractName, "Proxy")
	if proxyContract {
		contractName = strings.TrimSuffix(contractName, "Proxy")
	}
	refs, exists := standard.BytecodeImmutables[tag].ForContractWithName(contractName)
	if exists {
		err := json.Unmarshal([]byte(refs), &parsedImmutables)
		if err != nil {
			return nil, fmt.Errorf("unable to parse immutable references for %s: %w", contractName, err)
		}
	}
	return &BytecodeAndImmutableReferences{Bytecode: bytecode, ImmutableReferences: parsedImmutables}, nil
}

// maskBytecode masks the sections of the bytecode where immutables are stored.
// If immutables aren't present, the stored bytecode in the receiver is unaltered
func (deployed *BytecodeAndImmutableReferences) maskBytecode(contractName string) error {
	for _, v := range deployed.ImmutableReferences {
		for _, r := range v {
			for i := r.Start; i < r.Start+r.Length; i++ {
				if i >= len(deployed.Bytecode) {
					return fmt.Errorf("immutable reference for contract %s [start:%d, length: %d] extends beyond bytecode", contractName, r.Start, r.Length)
				}
				deployed.Bytecode[i] = 0
			}
		}
	}
	return nil
}
