package main

import (
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"time"
)

// Targets defines the pass/fail thresholds.
var Targets = struct {
	BaselineP50      time.Duration
	BaselineP99      time.Duration
	BaselineMaxLat   time.Duration
	BaselineErrRate  float64
	SustainedP99     time.Duration
	SustainedErrRate float64
	DegradationPct   float64
}{
	BaselineP50:      50 * time.Millisecond,
	BaselineP99:      200 * time.Millisecond,
	BaselineMaxLat:   1 * time.Second,
	BaselineErrRate:  0.0,
	SustainedP99:     200 * time.Millisecond,
	SustainedErrRate: 0.1,
	DegradationPct:   100.0, // <2x = <100% degradation
}

// TestResults holds all scenario results.
type TestResults struct {
	BaselineMetrics   *Metrics
	SustainedMetrics  *Metrics
	ConcurrentMetrics *Metrics
	APIURL            string
	Duration          time.Duration
	TargetRPS         int
	ReaderCount       int
	MeterDisabled     bool
}

// GenerateReport creates a markdown report from test results.
func GenerateReport(results TestResults, outputDir string) (string, error) {
	now := time.Now()
	filename := fmt.Sprintf("%s-load-test.md", now.Format("2006-01-02"))
	outputPath := filepath.Join(outputDir, filename)

	var sb strings.Builder

	// Header
	sb.WriteString("# UAT Report: Load Testing Results\n\n")
	sb.WriteString(fmt.Sprintf("**Date:** %s\n", now.Format("2006-01-02")))
	sb.WriteString("**Phase/Feature:** P4.1 Load Testing\n")
	sb.WriteString("**Tester:** load-test CLI\n")

	// Calculate overall status
	overallPass := true
	if results.BaselineMetrics != nil {
		if results.BaselineMetrics.WriteP99() > Targets.BaselineP99 ||
			results.BaselineMetrics.WriteErrorRate() > Targets.BaselineErrRate {
			overallPass = false
		}
	}
	if results.SustainedMetrics != nil {
		if results.SustainedMetrics.WriteP99() > Targets.SustainedP99 ||
			results.SustainedMetrics.WriteErrorRate() > Targets.SustainedErrRate {
			overallPass = false
		}
	}
	if results.ConcurrentMetrics != nil {
		if results.ConcurrentMetrics.DegradationPct() > Targets.DegradationPct {
			overallPass = false
		}
	}

	if overallPass {
		sb.WriteString("**Status:** PASS\n\n")
	} else {
		sb.WriteString("**Status:** FAIL\n\n")
	}

	// Environment
	sb.WriteString("## Environment\n\n")
	sb.WriteString(fmt.Sprintf("- Go version: %s\n", runtime.Version()))
	sb.WriteString(fmt.Sprintf("- OS: %s/%s\n", runtime.GOOS, runtime.GOARCH))
	sb.WriteString(fmt.Sprintf("- StemeDB API: %s\n", results.APIURL))
	if results.MeterDisabled {
		sb.WriteString("- Meter: Disabled (`STEMEDB_METER_ENABLED=false`)\n")
	} else {
		sb.WriteString("- Meter: Enabled (may affect sustained test)\n")
	}
	sb.WriteString("\n")

	// Test Results
	sb.WriteString("## Test Results\n\n")

	// Scenario A: Baseline
	if results.BaselineMetrics != nil {
		sb.WriteString(writeBaselineSection(results.BaselineMetrics))
	}

	// Scenario B: Sustained
	if results.SustainedMetrics != nil {
		sb.WriteString(writeSustainedSection(results.SustainedMetrics, results.Duration, results.TargetRPS))
	}

	// Scenario C: Concurrent
	if results.ConcurrentMetrics != nil {
		sb.WriteString(writeConcurrentSection(results.ConcurrentMetrics))
	}

	// Recommendations
	sb.WriteString(writeRecommendations(results))

	// Write file
	if err := os.MkdirAll(outputDir, 0755); err != nil {
		return "", fmt.Errorf("failed to create output directory: %w", err)
	}

	if err := os.WriteFile(outputPath, []byte(sb.String()), 0644); err != nil {
		return "", fmt.Errorf("failed to write report: %w", err)
	}

	return outputPath, nil
}

func writeBaselineSection(m *Metrics) string {
	var sb strings.Builder

	sb.WriteString("### Scenario A: Baseline Latency (10K Assertions)\n\n")
	sb.WriteString("| Metric | Target | Actual | Status |\n")
	sb.WriteString("|--------|--------|--------|--------|\n")

	// Total count
	sb.WriteString(fmt.Sprintf("| Total | 10,000 | %d | %s |\n",
		m.WriteCount(),
		passFailEmoji(m.WriteCount() >= 10000)))

	// P50 latency
	p50Pass := m.WriteP50() <= Targets.BaselineP50
	sb.WriteString(fmt.Sprintf("| p50 latency | <%s | %s | %s |\n",
		Targets.BaselineP50,
		m.WriteP50(),
		passFailEmoji(p50Pass)))

	// P99 latency
	p99Pass := m.WriteP99() <= Targets.BaselineP99
	sb.WriteString(fmt.Sprintf("| p99 latency | <%s | %s | %s |\n",
		Targets.BaselineP99,
		m.WriteP99(),
		passFailEmoji(p99Pass)))

	// Max latency
	maxPass := m.WriteMax() <= Targets.BaselineMaxLat
	sb.WriteString(fmt.Sprintf("| Max latency | <%s | %s | %s |\n",
		Targets.BaselineMaxLat,
		m.WriteMax(),
		passFailEmoji(maxPass)))

	// Error rate
	errPass := m.WriteErrorRate() <= Targets.BaselineErrRate
	sb.WriteString(fmt.Sprintf("| Error rate | 0%% | %.2f%% | %s |\n",
		m.WriteErrorRate(),
		passFailEmoji(errPass)))

	sb.WriteString("\n")
	return sb.String()
}

func writeSustainedSection(m *Metrics, duration time.Duration, targetRPS int) string {
	var sb strings.Builder

	sb.WriteString("### Scenario B: Sustained Writes\n\n")
	sb.WriteString("| Metric | Target | Actual | Status |\n")
	sb.WriteString("|--------|--------|--------|--------|\n")

	// Duration
	sb.WriteString(fmt.Sprintf("| Duration | %s | %s | PASS |\n",
		duration.Round(time.Second),
		m.Duration().Round(time.Second)))

	// Throughput
	actualRPS := m.WriteThroughput()
	rpsPass := actualRPS >= float64(targetRPS)*0.9 // Allow 10% tolerance
	sb.WriteString(fmt.Sprintf("| Avg throughput | %d/sec | %.0f/sec | %s |\n",
		targetRPS,
		actualRPS,
		passFailEmoji(rpsPass)))

	// P99 latency
	p99Pass := m.WriteP99() <= Targets.SustainedP99
	sb.WriteString(fmt.Sprintf("| p99 latency | <%s | %s | %s |\n",
		Targets.SustainedP99,
		m.WriteP99(),
		passFailEmoji(p99Pass)))

	// Error rate
	errPass := m.WriteErrorRate() <= Targets.SustainedErrRate
	sb.WriteString(fmt.Sprintf("| Error rate | <%.1f%% | %.3f%% | %s |\n",
		Targets.SustainedErrRate,
		m.WriteErrorRate(),
		passFailEmoji(errPass)))

	// Total writes
	sb.WriteString(fmt.Sprintf("| Total writes | - | %d | - |\n", m.WriteCount()))

	sb.WriteString("\n")
	return sb.String()
}

func writeConcurrentSection(m *Metrics) string {
	var sb strings.Builder

	sb.WriteString("### Scenario C: Concurrent Readers\n\n")
	sb.WriteString("| Metric | Target | Actual | Status |\n")
	sb.WriteString("|--------|--------|--------|--------|\n")

	// Reader count
	sb.WriteString(fmt.Sprintf("| Reader count | 100 | %d | PASS |\n", m.ReaderCount()))

	// Baseline P99
	sb.WriteString(fmt.Sprintf("| Baseline p99 | - | %s | - |\n", m.BaselineP99()))

	// Under-load P99
	degradationPass := m.DegradationPct() <= Targets.DegradationPct
	sb.WriteString(fmt.Sprintf("| Under-load p99 | <2x baseline | %s | %s |\n",
		m.LoadedP99(),
		passFailEmoji(degradationPass)))

	// Degradation percentage
	sb.WriteString(fmt.Sprintf("| Degradation | <100%% | %.1f%% | %s |\n",
		m.DegradationPct(),
		passFailEmoji(degradationPass)))

	sb.WriteString("\n")
	return sb.String()
}

func writeRecommendations(results TestResults) string {
	var sb strings.Builder
	var recommendations []string

	sb.WriteString("## Recommendations\n\n")

	// Check baseline
	if results.BaselineMetrics != nil {
		if results.BaselineMetrics.WriteP99() > Targets.BaselineP99 {
			recommendations = append(recommendations,
				"Baseline p99 latency exceeds target. Consider profiling write path for bottlenecks.")
		}
		if results.BaselineMetrics.WriteErrorRate() > 0 {
			recommendations = append(recommendations,
				"Baseline test had errors. Investigate server logs for root cause.")
		}
	}

	// Check sustained
	if results.SustainedMetrics != nil {
		if results.SustainedMetrics.WriteP99() > Targets.SustainedP99 {
			recommendations = append(recommendations,
				"Sustained write p99 latency exceeds target. Consider scaling or optimizing WAL.")
		}
		if results.SustainedMetrics.WriteErrorRate() > Targets.SustainedErrRate {
			recommendations = append(recommendations,
				"Sustained write error rate too high. Check for resource exhaustion or rate limiting.")
		}
		if !results.MeterDisabled {
			recommendations = append(recommendations,
				"Meter was enabled during sustained test. Re-run with STEMEDB_METER_ENABLED=false for accurate results.")
		}
	}

	// Check concurrent
	if results.ConcurrentMetrics != nil {
		if results.ConcurrentMetrics.DegradationPct() > Targets.DegradationPct {
			recommendations = append(recommendations,
				"Read latency degradation under load exceeds 2x. Consider read replica or caching layer.")
		}
	}

	if len(recommendations) == 0 {
		sb.WriteString("All tests passed. System is ready for production load.\n\n")
	} else {
		for _, rec := range recommendations {
			sb.WriteString(fmt.Sprintf("- %s\n", rec))
		}
		sb.WriteString("\n")
	}

	return sb.String()
}

func passFailEmoji(pass bool) string {
	if pass {
		return "PASS"
	}
	return "FAIL"
}
