package audit

import (
	"crypto/sha256"
	"fmt"
	"sync"
	"time"
)

// ImmutableLogEntry represents a tamper-proof audit entry
type ImmutableLogEntry struct {
	Sequence  int64                  `json:"sequence"`
	PrevHash  string                 `json:"prev_hash"`
	Content   string                 `json:"content"`
	Hash      string                 `json:"hash"`
	Timestamp time.Time              `json:"timestamp"`
	Signature string                 `json:"signature,omitempty"`
	Metadata  map[string]interface{} `json:"metadata,omitempty"`
}

// ImmutableLog maintains a tamper-proof audit trail
type ImmutableLog struct {
	mu          sync.RWMutex
	entries     []*ImmutableLogEntry
	logPath     string
	sequence    int64
	prevHash    string
	workflowKey string
}

// NewImmutableLog creates a new immutable log
func NewImmutableLog(logPath string, workflowKey string) *ImmutableLog {
	return &ImmutableLog{
		entries:     make([]*ImmutableLogEntry, 0),
		logPath:     logPath,
		sequence:    0,
		prevHash:    "genesis",
		workflowKey: workflowKey,
	}
}

// Append adds an entry to the immutable log
func (il *ImmutableLog) Append(content string, metadata map[string]interface{}) (*ImmutableLogEntry, error) {
	il.mu.Lock()
	defer il.mu.Unlock()

	il.sequence++
	hash := il.computeHash(il.sequence, il.prevHash, content)

	entry := &ImmutableLogEntry{
		Sequence:  il.sequence,
		PrevHash:  il.prevHash,
		Content:   content,
		Hash:      hash,
		Timestamp: time.Now(),
		Metadata:  metadata,
	}

	il.entries = append(il.entries, entry)
	il.prevHash = hash

	return entry, nil
}

// Verify verifies the integrity of the log
func (il *ImmutableLog) Verify() (bool, error) {
	il.mu.RLock()
	defer il.mu.RUnlock()

	prevHash := "genesis"

	for _, entry := range il.entries {
		expectedHash := il.computeHash(entry.Sequence, entry.PrevHash, entry.Content)

		if entry.Hash != expectedHash || entry.PrevHash != prevHash {
			return false, fmt.Errorf("integrity check failed at sequence %d", entry.Sequence)
		}

		prevHash = entry.Hash
	}

	return true, nil
}

// GetEntries returns all entries
func (il *ImmutableLog) GetEntries() []*ImmutableLogEntry {
	il.mu.RLock()
	defer il.mu.RUnlock()

	result := make([]*ImmutableLogEntry, len(il.entries))
	copy(result, il.entries)

	return result
}

// GetLastHash returns the last hash
func (il *ImmutableLog) GetLastHash() string {
	il.mu.RLock()
	defer il.mu.RUnlock()

	return il.prevHash
}

// computeHash computes SHA256 hash
func (il *ImmutableLog) computeHash(seq int64, prevHash, content string) string {
	data := fmt.Sprintf("%d:%s:%s:%s", seq, prevHash, content, il.workflowKey)
	hash := sha256.Sum256([]byte(data))
	return fmt.Sprintf("%x", hash)
}
