// Package main demonstrates the Synor Compiler SDK for Go
//
// This example covers smart contract compilation and analysis:
// - WASM contract compilation and optimization
// - ABI extraction and encoding
// - Contract analysis and security scanning
// - Validation and verification
package main

import (
	"context"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/synor/sdk-go/compiler"
)

func main() {
	// Initialize client
	config := compiler.Config{
		APIKey:                   getEnv("SYNOR_API_KEY", "your-api-key"),
		Endpoint:                 "https://compiler.synor.io/v1",
		Timeout:                  60 * time.Second,
		Retries:                  3,
		Debug:                    false,
		DefaultOptimizationLevel: compiler.OptimizationSize,
		MaxContractSize:          256 * 1024,
		UseWasmOpt:               true,
		Validate:                 true,
		ExtractMetadata:          true,
		GenerateABI:              true,
	}

	client, err := compiler.NewClient(config)
	if err != nil {
		log.Fatalf("Failed to create client: %v", err)
	}
	defer client.Close()

	ctx := context.Background()

	// Check service health
	healthy, err := client.HealthCheck(ctx)
	if err != nil {
		log.Printf("Health check failed: %v", err)
	} else {
		fmt.Printf("Service healthy: %v\n\n", healthy)
	}

	// Run examples
	compileContractExample(ctx, client)
	compilationModesExample(ctx, client)
	abiExample(ctx, client)
	analysisExample(ctx, client)
	validationExample(ctx, client)
	securityExample(ctx, client)
}

// createMinimalWasm creates a minimal valid WASM module for testing.
func createMinimalWasm() []byte {
	return []byte{
		0x00, 0x61, 0x73, 0x6d, // Magic: \0asm
		0x01, 0x00, 0x00, 0x00, // Version: 1
		// Type section
		0x01, 0x07, 0x01, 0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f,
		// Function section
		0x03, 0x02, 0x01, 0x00,
		// Export section
		0x07, 0x08, 0x01, 0x04, 0x61, 0x64, 0x64, 0x00, 0x00,
		// Code section
		0x0a, 0x09, 0x01, 0x07, 0x00, 0x20, 0x00, 0x20, 0x01, 0x6a, 0x0b,
	}
}

func compileContractExample(ctx context.Context, client *compiler.Client) {
	fmt.Println("=== Contract Compilation ===")

	wasm := createMinimalWasm()

	result, err := client.Compile(ctx, wasm, &compiler.CompileOptions{
		OptimizationLevel: compiler.OptimizationSize,
		UseWasmOpt:        true,
		Validate:          true,
		ExtractMetadata:   true,
		GenerateABI:       true,
		StripOptions: &compiler.StripOptions{
			StripDebug:       true,
			StripProducers:   true,
			StripNames:       true,
			StripCustom:      true,
			StripUnused:      true,
			PreserveSections: []string{},
		},
	})
	if err != nil {
		log.Printf("Failed to compile contract: %v", err)
		return
	}

	fmt.Println("Compilation result:")
	fmt.Printf("  Contract ID: %s\n", result.ContractID)
	fmt.Printf("  Code hash: %s\n", result.CodeHash)
	fmt.Printf("  Original size: %d bytes\n", result.OriginalSize)
	fmt.Printf("  Optimized size: %d bytes\n", result.OptimizedSize)
	fmt.Printf("  Size reduction: %.1f%%\n", result.SizeReduction)
	fmt.Printf("  Estimated deploy gas: %d\n", result.EstimatedDeployGas)

	if result.Metadata != nil {
		fmt.Println("\nMetadata:")
		fmt.Printf("  Name: %s\n", result.Metadata.Name)
		fmt.Printf("  Version: %s\n", result.Metadata.Version)
		fmt.Printf("  SDK Version: %s\n", result.Metadata.SDKVersion)
	}

	if result.ABI != nil {
		fmt.Println("\nABI:")
		fmt.Printf("  Functions: %d\n", len(result.ABI.Functions))
		fmt.Printf("  Events: %d\n", len(result.ABI.Events))
		fmt.Printf("  Errors: %d\n", len(result.ABI.Errors))
	}

	fmt.Println()
}

func compilationModesExample(ctx context.Context, client *compiler.Client) {
	fmt.Println("=== Compilation Modes ===")

	wasm := createMinimalWasm()

	// Development mode: fast compilation, debugging support
	fmt.Println("Development mode:")
	devResult, err := client.Contracts.CompileDev(ctx, wasm)
	if err != nil {
		log.Printf("Failed to compile in dev mode: %v", err)
		return
	}
	fmt.Printf("  Size: %d bytes\n", devResult.OptimizedSize)
	fmt.Println("  Optimization: none")

	// Production mode: maximum optimization
	fmt.Println("\nProduction mode:")
	prodResult, err := client.Contracts.CompileProduction(ctx, wasm)
	if err != nil {
		log.Printf("Failed to compile in production mode: %v", err)
		return
	}
	fmt.Printf("  Size: %d bytes\n", prodResult.OptimizedSize)
	fmt.Println("  Optimization: aggressive")
	fmt.Printf("  Size savings: %d bytes\n", devResult.OptimizedSize-prodResult.OptimizedSize)

	// Custom optimization levels
	fmt.Println("\nOptimization levels:")
	levels := []compiler.OptimizationLevel{
		compiler.OptimizationNone,
		compiler.OptimizationBasic,
		compiler.OptimizationSize,
		compiler.OptimizationAggressive,
	}

	for _, level := range levels {
		result, err := client.Compile(ctx, wasm, &compiler.CompileOptions{
			OptimizationLevel: level,
		})
		if err != nil {
			log.Printf("Failed to compile with %s: %v", level, err)
			continue
		}
		fmt.Printf("  %s: %d bytes\n", level, result.OptimizedSize)
	}

	fmt.Println()
}

func abiExample(ctx context.Context, client *compiler.Client) {
	fmt.Println("=== ABI Operations ===")

	wasm := createMinimalWasm()

	// Extract ABI from WASM
	abi, err := client.ABI.Extract(ctx, wasm)
	if err != nil {
		log.Printf("Failed to extract ABI: %v", err)
		return
	}
	fmt.Printf("Contract: %s\n", abi.Name)
	fmt.Printf("Version: %s\n", abi.Version)

	// List functions
	if len(abi.Functions) > 0 {
		fmt.Println("\nFunctions:")
		for _, fn := range abi.Functions {
			inputs := ""
			for i, input := range fn.Inputs {
				if i > 0 {
					inputs += ", "
				}
				inputs += fmt.Sprintf("%s: %s", input.Name, input.Type.TypeName)
			}

			outputs := "void"
			if len(fn.Outputs) > 0 {
				outputTypes := make([]string, len(fn.Outputs))
				for i, output := range fn.Outputs {
					outputTypes[i] = output.Type.TypeName
				}
				outputs = fmt.Sprintf("%v", outputTypes)
			}

			modifiers := ""
			if fn.View {
				modifiers += "view "
			}
			if fn.Payable {
				modifiers += "payable"
			}

			fmt.Printf("  %s(%s) -> %s %s\n", fn.Name, inputs, outputs, modifiers)
			fmt.Printf("    Selector: %s\n", fn.Selector)
		}
	}

	// List events
	if len(abi.Events) > 0 {
		fmt.Println("\nEvents:")
		for _, event := range abi.Events {
			params := ""
			for i, param := range event.Params {
				if i > 0 {
					params += ", "
				}
				indexed := ""
				if param.Indexed {
					indexed = "indexed "
				}
				params += fmt.Sprintf("%s%s: %s", indexed, param.Name, param.Type.TypeName)
			}
			fmt.Printf("  %s(%s)\n", event.Name, params)
			fmt.Printf("    Topic: %s\n", event.Topic)
		}
	}

	// Encode a function call
	if len(abi.Functions) > 0 {
		fn := abi.Functions[0]
		encoded, err := client.ABI.EncodeCall(ctx, fn, []interface{}{"arg1", "arg2"})
		if err != nil {
			log.Printf("Failed to encode call: %v", err)
		} else {
			fmt.Printf("\nEncoded call to %s: %s\n", fn.Name, encoded)
		}

		// Decode a result
		decoded, err := client.ABI.DecodeResult(ctx, fn, encoded)
		if err != nil {
			log.Printf("Failed to decode result: %v", err)
		} else {
			fmt.Printf("Decoded result: %v\n", decoded)
		}
	}

	fmt.Println()
}

func analysisExample(ctx context.Context, client *compiler.Client) {
	fmt.Println("=== Contract Analysis ===")

	wasm := createMinimalWasm()

	// Full analysis
	analysis, err := client.Analysis.Analyze(ctx, wasm)
	if err != nil {
		log.Printf("Failed to analyze contract: %v", err)
		return
	}

	// Size breakdown
	if analysis.SizeBreakdown != nil {
		fmt.Println("Size breakdown:")
		fmt.Printf("  Code: %d bytes\n", analysis.SizeBreakdown.Code)
		fmt.Printf("  Data: %d bytes\n", analysis.SizeBreakdown.Data)
		fmt.Printf("  Functions: %d bytes\n", analysis.SizeBreakdown.Functions)
		fmt.Printf("  Memory: %d bytes\n", analysis.SizeBreakdown.Memory)
		fmt.Printf("  Exports: %d bytes\n", analysis.SizeBreakdown.Exports)
		fmt.Printf("  Imports: %d bytes\n", analysis.SizeBreakdown.Imports)
		fmt.Printf("  Total: %d bytes\n", analysis.SizeBreakdown.Total)
	}

	// Function analysis
	if len(analysis.Functions) > 0 {
		fmt.Println("\nFunction analysis:")
		limit := 5
		if len(analysis.Functions) < limit {
			limit = len(analysis.Functions)
		}
		for _, fn := range analysis.Functions[:limit] {
			fmt.Printf("  %s:\n", fn.Name)
			fmt.Printf("    Size: %d bytes\n", fn.Size)
			fmt.Printf("    Instructions: %d\n", fn.InstructionCount)
			fmt.Printf("    Locals: %d\n", fn.LocalCount)
			fmt.Printf("    Exported: %v\n", fn.Exported)
			fmt.Printf("    Estimated gas: %d\n", fn.EstimatedGas)
		}
	}

	// Import analysis
	if len(analysis.Imports) > 0 {
		fmt.Println("\nImports:")
		for _, imp := range analysis.Imports {
			fmt.Printf("  %s.%s (%s)\n", imp.Module, imp.Name, imp.Kind)
		}
	}

	// Gas analysis
	if analysis.GasAnalysis != nil {
		fmt.Println("\nGas analysis:")
		fmt.Printf("  Deployment: %d\n", analysis.GasAnalysis.DeploymentGas)
		fmt.Printf("  Memory init: %d\n", analysis.GasAnalysis.MemoryInitGas)
		fmt.Printf("  Data section: %d\n", analysis.GasAnalysis.DataSectionGas)
	}

	// Extract metadata
	metadata, err := client.Analysis.ExtractMetadata(ctx, wasm)
	if err != nil {
		log.Printf("Failed to extract metadata: %v", err)
	} else {
		fmt.Println("\nContract metadata:")
		fmt.Printf("  Name: %s\n", metadata.Name)
		fmt.Printf("  Version: %s\n", metadata.Version)
		fmt.Printf("  Build timestamp: %s\n", metadata.BuildTimestamp)
	}

	// Estimate deployment gas
	gas, err := client.Analysis.EstimateDeployGas(ctx, wasm)
	if err != nil {
		log.Printf("Failed to estimate deploy gas: %v", err)
	} else {
		fmt.Printf("\nEstimated deployment gas: %d\n", gas)
	}

	fmt.Println()
}

func validationExample(ctx context.Context, client *compiler.Client) {
	fmt.Println("=== Contract Validation ===")

	wasm := createMinimalWasm()

	// Full validation
	result, err := client.Validation.Validate(ctx, wasm)
	if err != nil {
		log.Printf("Failed to validate contract: %v", err)
		return
	}
	fmt.Printf("Valid: %v\n", result.Valid)
	fmt.Printf("Exports: %d\n", result.ExportCount)
	fmt.Printf("Imports: %d\n", result.ImportCount)
	fmt.Printf("Functions: %d\n", result.FunctionCount)
	fmt.Printf("Memory pages: %d\n", result.MemoryPages)

	if len(result.Errors) > 0 {
		fmt.Println("\nValidation errors:")
		for _, e := range result.Errors {
			fmt.Printf("  [%s] %s\n", e.Code, e.Message)
			if e.Location != "" {
				fmt.Printf("    at %s\n", e.Location)
			}
		}
	}

	if len(result.Warnings) > 0 {
		fmt.Println("\nWarnings:")
		for _, warning := range result.Warnings {
			fmt.Printf("  %s\n", warning)
		}
	}

	// Quick validation
	isValid, err := client.Validation.IsValid(ctx, wasm)
	if err != nil {
		log.Printf("Failed quick validation: %v", err)
	} else {
		fmt.Printf("\nQuick validation: %v\n", isValid)
	}

	// Get validation errors only
	errors, err := client.Validation.GetErrors(ctx, wasm)
	if err != nil {
		log.Printf("Failed to get validation errors: %v", err)
	} else {
		fmt.Printf("Error count: %d\n", len(errors))
	}

	// Validate required exports
	hasRequired, err := client.Validation.ValidateExports(ctx, wasm, []string{"init", "execute", "query"})
	if err != nil {
		log.Printf("Failed to validate exports: %v", err)
	} else {
		fmt.Printf("Has required exports: %v\n", hasRequired)
	}

	// Validate memory constraints
	memoryValid, err := client.Validation.ValidateMemory(ctx, wasm, 16)
	if err != nil {
		log.Printf("Failed to validate memory: %v", err)
	} else {
		fmt.Printf("Memory within 16 pages: %v\n", memoryValid)
	}

	fmt.Println()
}

func securityExample(ctx context.Context, client *compiler.Client) {
	fmt.Println("=== Security Scanning ===")

	wasm := createMinimalWasm()

	security, err := client.Analysis.SecurityScan(ctx, wasm)
	if err != nil {
		log.Printf("Failed to perform security scan: %v", err)
		return
	}

	fmt.Printf("Security score: %d/100\n", security.Score)

	if len(security.Issues) > 0 {
		fmt.Println("\nSecurity issues:")
		severityIcons := map[string]string{
			"critical": "[CRIT]",
			"high":     "[HIGH]",
			"medium":   "[MED]",
			"low":      "[LOW]",
		}
		for _, issue := range security.Issues {
			icon := severityIcons[issue.Severity]
			if icon == "" {
				icon = "[???]"
			}
			fmt.Printf("%s [%s] %s\n", icon, issue.Severity, issue.Type)
			fmt.Printf("   %s\n", issue.Description)
			if issue.Location != "" {
				fmt.Printf("   at %s\n", issue.Location)
			}
		}
	} else {
		fmt.Println("No security issues found!")
	}

	if len(security.Recommendations) > 0 {
		fmt.Println("\nRecommendations:")
		for _, rec := range security.Recommendations {
			fmt.Printf("  * %s\n", rec)
		}
	}

	fmt.Println()
}

func getEnv(key, defaultValue string) string {
	if value := os.Getenv(key); value != "" {
		return value
	}
	return defaultValue
}
