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

package releaseauth

import (
	"bytes"
	"crypto/sha256"
	"encoding/hex"
	"errors"
	"fmt"
	"log"
)

// SHA256Hash represents a 256-bit SHA hash
type SHA256Hash [sha256.Size]byte

// ErrInvalidSHA256Hash is returned when the hash is invalid
var ErrInvalidSHA256Hash = errors.New("the value was not a valid SHA-256 hash")

// SHA256FromHex decodes a SHA256Hash from a hex string dump
func SHA256FromHex(hashHex string) (SHA256Hash, error) {
	var result [sha256.Size]byte
	hash, err := hex.DecodeString(hashHex)
	if err != nil || len(hash) != sha256.Size {
		return result, ErrInvalidSHA256Hash
	}

	if copy(result[:], hash) != sha256.Size {
		panic("could not copy hash value")
	}

	return result, nil
}

// SHA256Checksums decodes a file generated by the sha256sum program
type SHA256Checksums map[string]SHA256Hash

func ParseChecksums(data []byte) (SHA256Checksums, error) {
	items := bytes.Split(data, []byte("\n"))
	result := make(map[string]SHA256Hash, len(items))

	for _, line := range items {
		parts := bytes.SplitN(line, []byte("  "), 2)

		if len(parts) != 2 {
			break
		}

		log.Printf("[TRACE] parsing SHA256SUMS %q = %q", parts[0], parts[1])
		hash, err := SHA256FromHex(string(parts[0]))
		if err != nil {
			return result, fmt.Errorf("failed to parse checksums: %w", err)
		}

		result[string(parts[1])] = hash
	}

	return result, nil
}

// Validate retrieves a SHA256Hash for the a filename and compares it
// to the specified hash. Validate returns an error if the hash is not found
// or if it does not match.
func (c SHA256Checksums) Validate(filename string, hash SHA256Hash) error {
	sum, ok := c[filename]
	if !ok {
		return fmt.Errorf("no checksum found for filename %q", filename)
	}

	if sum != hash {
		return fmt.Errorf("checksums do not match")
	}

	return nil
}
