package audit

import (
	"bufio"
	"encoding/json"
	"fmt"
	"os"
	"time"

	"github.com/jfraeys/fetch_ml/internal/logging"
)

// ChainVerifier provides continuous verification of audit log integrity
// by checking the chained hash structure and detecting any tampering.
type ChainVerifier struct {
	logger *logging.Logger
}

// NewChainVerifier creates a new audit chain verifier
func NewChainVerifier(logger *logging.Logger) *ChainVerifier {
	return &ChainVerifier{
		logger: logger,
	}
}

// VerificationResult contains the outcome of a chain verification
//type VerificationResult struct {
//	Timestamp      time.Time
//	TotalEvents    int
//	Valid          bool
//	FirstTampered  int64  // Sequence number of first tampered event, -1 if none
//	Error          string // Error message if verification failed
//	ChainRootHash  string // Hash of the last valid event (for external verification)
//}

// VerificationResult contains the outcome of a chain verification
type VerificationResult struct {
	Timestamp     time.Time
	Error         string
	ChainRootHash string
	TotalEvents   int
	FirstTampered int64
	Valid         bool
}

// VerifyLogFile performs a complete verification of an audit log file.
// It checks the integrity chain by verifying each event's hash and
// ensuring the previous hash links are unbroken.
func (cv *ChainVerifier) VerifyLogFile(logPath string) (*VerificationResult, error) {
	result := &VerificationResult{
		Timestamp:     time.Now().UTC(),
		TotalEvents:   0,
		Valid:         true,
		FirstTampered: -1,
	}

	// Open the log file
	file, err := os.Open(logPath)
	if err != nil {
		if os.IsNotExist(err) {
			// No log file yet - this is valid (no entries to verify)
			return result, nil
		}
		result.Valid = false
		result.Error = fmt.Sprintf("failed to open log file: %v", err)
		return result, err
	}
	defer file.Close()

	// Create a temporary logger to calculate hashes
	tempLogger, _ := NewLogger(false, "", cv.logger)

	var events []Event
	scanner := bufio.NewScanner(file)
	lineNum := 0

	for scanner.Scan() {
		lineNum++
		line := scanner.Text()
		if line == "" {
			continue
		}

		var event Event
		if err := json.Unmarshal([]byte(line), &event); err != nil {
			result.Valid = false
			result.Error = fmt.Sprintf("failed to parse event at line %d: %v", lineNum, err)
			return result, fmt.Errorf("parse error at line %d: %w", lineNum, err)
		}

		events = append(events, event)
		result.TotalEvents++
	}

	if err := scanner.Err(); err != nil {
		result.Valid = false
		result.Error = fmt.Sprintf("error reading log file: %v", err)
		return result, err
	}

	// Verify the chain
	tamperedSeq, err := tempLogger.VerifyChain(events)
	if err != nil {
		result.Valid = false
		result.FirstTampered = int64(tamperedSeq)
		result.Error = err.Error()
		return result, err
	}

	if tamperedSeq != -1 {
		result.Valid = false
		result.FirstTampered = int64(tamperedSeq)
		result.Error = fmt.Sprintf("tampering detected at sequence %d", tamperedSeq)
	}

	// Set the chain root hash (hash of the last event)
	if len(events) > 0 {
		lastEvent := events[len(events)-1]
		result.ChainRootHash = lastEvent.EventHash
	}

	return result, nil
}

// ContinuousVerification runs verification at regular intervals and reports any issues.
// This should be run as a background goroutine in long-running services.
func (cv *ChainVerifier) ContinuousVerification(logPath string, interval time.Duration, alertFunc func(*VerificationResult)) {
	if interval <= 0 {
		interval = 15 * time.Minute // Default: 15 minutes for HIPAA, use 1 hour otherwise
	}

	ticker := time.NewTicker(interval)
	defer ticker.Stop()

	// Run initial verification
	cv.runAndReport(logPath, alertFunc)

	for range ticker.C {
		cv.runAndReport(logPath, alertFunc)
	}
}

// runAndReport performs verification and calls the alert function if issues are found
func (cv *ChainVerifier) runAndReport(logPath string, alertFunc func(*VerificationResult)) {
	result, err := cv.VerifyLogFile(logPath)
	if err != nil {
		if cv.logger != nil {
			cv.logger.Error("audit chain verification error", "error", err, "log_path", logPath)
		}
		// Still report the error
		if alertFunc != nil {
			alertFunc(result)
		}
		return
	}

	// Report if not valid or if we just want to log successful verification periodically
	if !result.Valid {
		if cv.logger != nil {
			cv.logger.Error("audit chain tampering detected",
				"first_tampered", result.FirstTampered,
				"total_events", result.TotalEvents,
				"chain_root", result.ChainRootHash[:16])
		}
		if alertFunc != nil {
			alertFunc(result)
		}
	} else {
		if cv.logger != nil {
			cv.logger.Debug("audit chain verification passed",
				"total_events", result.TotalEvents,
				"chain_root", result.ChainRootHash[:16])
		}
	}
}

// VerifyAndAlert performs a single verification and returns true if tampering detected
func (cv *ChainVerifier) VerifyAndAlert(logPath string) (bool, error) {
	result, err := cv.VerifyLogFile(logPath)
	if err != nil {
		return true, err // Treat errors as potential tampering
	}

	return !result.Valid, nil
}

// GetChainRootHash returns the hash of the last event in the chain
// This can be published to an external append-only store for independent verification
func (cv *ChainVerifier) GetChainRootHash(logPath string) (string, error) {
	file, err := os.Open(logPath)
	if err != nil {
		return "", err
	}
	defer file.Close()

	var lastLine string
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := scanner.Text()
		if line != "" {
			lastLine = line
		}
	}

	if err := scanner.Err(); err != nil {
		return "", err
	}

	if lastLine == "" {
		return "", fmt.Errorf("no events in log file")
	}

	var event Event
	if err := json.Unmarshal([]byte(lastLine), &event); err != nil {
		return "", fmt.Errorf("failed to parse last event: %w", err)
	}

	return event.EventHash, nil
}
