package io.synor.examples;

import io.synor.compiler.*;
import io.synor.compiler.types.*;

import java.util.List;
import java.util.concurrent.CompletableFuture;

/**
 * Synor Compiler SDK Examples for Java
 *
 * Demonstrates smart contract compilation and analysis:
 * - WASM contract compilation and optimization
 * - ABI extraction and encoding
 * - Contract analysis and security scanning
 * - Validation and verification
 */
public class CompilerExample {

    public static void main(String[] args) throws Exception {
        // Initialize client
        CompilerConfig config = CompilerConfig.builder()
            .apiKey(System.getenv("SYNOR_API_KEY") != null ?
                    System.getenv("SYNOR_API_KEY") : "your-api-key")
            .endpoint("https://compiler.synor.io/v1")
            .timeout(60000)
            .retries(3)
            .debug(false)
            .defaultOptimizationLevel(OptimizationLevel.SIZE)
            .maxContractSize(256 * 1024)
            .useWasmOpt(true)
            .validate(true)
            .extractMetadata(true)
            .generateAbi(true)
            .build();

        SynorCompiler compiler = new SynorCompiler(config);

        try {
            // Check service health
            boolean healthy = compiler.healthCheck().get();
            System.out.println("Service healthy: " + healthy + "\n");

            // Run examples
            compileContractExample(compiler);
            compilationModesExample(compiler);
            abiExample(compiler);
            analysisExample(compiler);
            validationExample(compiler);
            securityExample(compiler);
        } finally {
            compiler.close();
        }
    }

    /**
     * Create a minimal valid WASM module for testing.
     */
    static byte[] createMinimalWasm() {
        return new 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
        };
    }

    static void compileContractExample(SynorCompiler compiler) throws Exception {
        System.out.println("=== Contract Compilation ===");

        byte[] wasm = createMinimalWasm();

        CompileResult result = compiler.compile(wasm, CompileOptions.builder()
            .optimizationLevel(OptimizationLevel.SIZE)
            .useWasmOpt(true)
            .validate(true)
            .extractMetadata(true)
            .generateAbi(true)
            .stripOptions(StripOptions.builder()
                .stripDebug(true)
                .stripProducers(true)
                .stripNames(true)
                .stripCustom(true)
                .stripUnused(true)
                .preserveSections(List.of())
                .build())
            .build()).get();

        System.out.println("Compilation result:");
        System.out.println("  Contract ID: " + result.getContractId());
        System.out.println("  Code hash: " + result.getCodeHash());
        System.out.println("  Original size: " + result.getOriginalSize() + " bytes");
        System.out.println("  Optimized size: " + result.getOptimizedSize() + " bytes");
        System.out.println("  Size reduction: " + String.format("%.1f", result.getSizeReduction()) + "%");
        System.out.println("  Estimated deploy gas: " + result.getEstimatedDeployGas());

        if (result.getMetadata() != null) {
            System.out.println("\nMetadata:");
            System.out.println("  Name: " + result.getMetadata().getName());
            System.out.println("  Version: " + result.getMetadata().getVersion());
            System.out.println("  SDK Version: " + result.getMetadata().getSdkVersion());
        }

        if (result.getAbi() != null) {
            System.out.println("\nABI:");
            System.out.println("  Functions: " + (result.getAbi().getFunctions() != null ?
                              result.getAbi().getFunctions().size() : 0));
            System.out.println("  Events: " + (result.getAbi().getEvents() != null ?
                              result.getAbi().getEvents().size() : 0));
            System.out.println("  Errors: " + (result.getAbi().getErrors() != null ?
                              result.getAbi().getErrors().size() : 0));
        }

        System.out.println();
    }

    static void compilationModesExample(SynorCompiler compiler) throws Exception {
        System.out.println("=== Compilation Modes ===");

        byte[] wasm = createMinimalWasm();

        // Development mode: fast compilation, debugging support
        System.out.println("Development mode:");
        CompileResult devResult = compiler.contracts().compileDev(wasm).get();
        System.out.println("  Size: " + devResult.getOptimizedSize() + " bytes");
        System.out.println("  Optimization: none");

        // Production mode: maximum optimization
        System.out.println("\nProduction mode:");
        CompileResult prodResult = compiler.contracts().compileProduction(wasm).get();
        System.out.println("  Size: " + prodResult.getOptimizedSize() + " bytes");
        System.out.println("  Optimization: aggressive");
        System.out.println("  Size savings: " + (devResult.getOptimizedSize() - prodResult.getOptimizedSize()) + " bytes");

        // Custom optimization levels
        System.out.println("\nOptimization levels:");
        OptimizationLevel[] levels = {
            OptimizationLevel.NONE,
            OptimizationLevel.BASIC,
            OptimizationLevel.SIZE,
            OptimizationLevel.AGGRESSIVE
        };

        for (OptimizationLevel level : levels) {
            CompileResult result = compiler.compile(wasm, CompileOptions.builder()
                .optimizationLevel(level)
                .build()).get();
            System.out.println("  " + level + ": " + result.getOptimizedSize() + " bytes");
        }

        System.out.println();
    }

    static void abiExample(SynorCompiler compiler) throws Exception {
        System.out.println("=== ABI Operations ===");

        byte[] wasm = createMinimalWasm();

        // Extract ABI from WASM
        ContractAbi abi = compiler.abi().extract(wasm).get();
        System.out.println("Contract: " + abi.getName());
        System.out.println("Version: " + abi.getVersion());

        // List functions
        if (abi.getFunctions() != null && !abi.getFunctions().isEmpty()) {
            System.out.println("\nFunctions:");
            for (AbiFunction func : abi.getFunctions()) {
                StringBuilder inputs = new StringBuilder();
                if (func.getInputs() != null) {
                    for (int i = 0; i < func.getInputs().size(); i++) {
                        AbiParam param = func.getInputs().get(i);
                        if (i > 0) inputs.append(", ");
                        inputs.append(param.getName()).append(": ").append(param.getType().getTypeName());
                    }
                }

                StringBuilder outputs = new StringBuilder();
                if (func.getOutputs() != null && !func.getOutputs().isEmpty()) {
                    for (int i = 0; i < func.getOutputs().size(); i++) {
                        if (i > 0) outputs.append(", ");
                        outputs.append(func.getOutputs().get(i).getType().getTypeName());
                    }
                } else {
                    outputs.append("void");
                }

                StringBuilder modifiers = new StringBuilder();
                if (func.isView()) modifiers.append("view ");
                if (func.isPayable()) modifiers.append("payable");

                System.out.println("  " + func.getName() + "(" + inputs + ") -> " + outputs + " " + modifiers);
                System.out.println("    Selector: " + func.getSelector());
            }
        }

        // List events
        if (abi.getEvents() != null && !abi.getEvents().isEmpty()) {
            System.out.println("\nEvents:");
            for (AbiEvent event : abi.getEvents()) {
                StringBuilder params = new StringBuilder();
                if (event.getParams() != null) {
                    for (int i = 0; i < event.getParams().size(); i++) {
                        AbiEventParam param = event.getParams().get(i);
                        if (i > 0) params.append(", ");
                        if (param.isIndexed()) params.append("indexed ");
                        params.append(param.getName()).append(": ").append(param.getType().getTypeName());
                    }
                }
                System.out.println("  " + event.getName() + "(" + params + ")");
                System.out.println("    Topic: " + event.getTopic());
            }
        }

        // Encode a function call
        if (abi.getFunctions() != null && !abi.getFunctions().isEmpty()) {
            AbiFunction func = abi.getFunctions().get(0);
            byte[] encoded = compiler.abi().encodeCall(func, List.of("arg1", "arg2")).get();
            System.out.println("\nEncoded call to " + func.getName() + ": " + bytesToHex(encoded));

            // Decode a result
            Object decoded = compiler.abi().decodeResult(func, encoded).get();
            System.out.println("Decoded result: " + decoded);
        }

        System.out.println();
    }

    static void analysisExample(SynorCompiler compiler) throws Exception {
        System.out.println("=== Contract Analysis ===");

        byte[] wasm = createMinimalWasm();

        // Full analysis
        ContractAnalysis analysis = compiler.analysis().analyze(wasm).get();

        // Size breakdown
        if (analysis.getSizeBreakdown() != null) {
            SizeBreakdown size = analysis.getSizeBreakdown();
            System.out.println("Size breakdown:");
            System.out.println("  Code: " + size.getCode() + " bytes");
            System.out.println("  Data: " + size.getData() + " bytes");
            System.out.println("  Functions: " + size.getFunctions() + " bytes");
            System.out.println("  Memory: " + size.getMemory() + " bytes");
            System.out.println("  Exports: " + size.getExports() + " bytes");
            System.out.println("  Imports: " + size.getImports() + " bytes");
            System.out.println("  Total: " + size.getTotal() + " bytes");
        }

        // Function analysis
        if (analysis.getFunctions() != null && !analysis.getFunctions().isEmpty()) {
            System.out.println("\nFunction analysis:");
            for (FunctionAnalysis func : analysis.getFunctions().subList(0,
                    Math.min(5, analysis.getFunctions().size()))) {
                System.out.println("  " + func.getName() + ":");
                System.out.println("    Size: " + func.getSize() + " bytes");
                System.out.println("    Instructions: " + func.getInstructionCount());
                System.out.println("    Locals: " + func.getLocalCount());
                System.out.println("    Exported: " + func.isExported());
                System.out.println("    Estimated gas: " + func.getEstimatedGas());
            }
        }

        // Import analysis
        if (analysis.getImports() != null && !analysis.getImports().isEmpty()) {
            System.out.println("\nImports:");
            for (ImportAnalysis imp : analysis.getImports()) {
                System.out.println("  " + imp.getModule() + "." + imp.getName() + " (" + imp.getKind() + ")");
            }
        }

        // Gas analysis
        if (analysis.getGasAnalysis() != null) {
            GasAnalysis gas = analysis.getGasAnalysis();
            System.out.println("\nGas analysis:");
            System.out.println("  Deployment: " + gas.getDeploymentGas());
            System.out.println("  Memory init: " + gas.getMemoryInitGas());
            System.out.println("  Data section: " + gas.getDataSectionGas());
        }

        // Extract metadata
        ContractMetadata metadata = compiler.analysis().extractMetadata(wasm).get();
        System.out.println("\nContract metadata:");
        System.out.println("  Name: " + metadata.getName());
        System.out.println("  Version: " + metadata.getVersion());
        System.out.println("  Build timestamp: " + metadata.getBuildTimestamp());

        // Estimate deployment gas
        long gasEstimate = compiler.analysis().estimateDeployGas(wasm).get();
        System.out.println("\nEstimated deployment gas: " + gasEstimate);

        System.out.println();
    }

    static void validationExample(SynorCompiler compiler) throws Exception {
        System.out.println("=== Contract Validation ===");

        byte[] wasm = createMinimalWasm();

        // Full validation
        ValidationResult result = compiler.validation().validate(wasm).get();
        System.out.println("Valid: " + result.isValid());
        System.out.println("Exports: " + result.getExportCount());
        System.out.println("Imports: " + result.getImportCount());
        System.out.println("Functions: " + result.getFunctionCount());
        System.out.println("Memory pages: " + result.getMemoryPages());

        if (result.getErrors() != null && !result.getErrors().isEmpty()) {
            System.out.println("\nValidation errors:");
            for (ValidationError error : result.getErrors()) {
                System.out.println("  [" + error.getCode() + "] " + error.getMessage());
                if (error.getLocation() != null) {
                    System.out.println("    at " + error.getLocation());
                }
            }
        }

        if (result.getWarnings() != null && !result.getWarnings().isEmpty()) {
            System.out.println("\nWarnings:");
            for (String warning : result.getWarnings()) {
                System.out.println("  " + warning);
            }
        }

        // Quick validation
        boolean isValid = compiler.validation().isValid(wasm).get();
        System.out.println("\nQuick validation: " + isValid);

        // Get validation errors only
        List<ValidationError> errors = compiler.validation().getErrors(wasm).get();
        System.out.println("Error count: " + errors.size());

        // Validate required exports
        boolean hasRequired = compiler.validation().validateExports(
            wasm,
            List.of("init", "execute", "query")
        ).get();
        System.out.println("Has required exports: " + hasRequired);

        // Validate memory constraints
        boolean memoryValid = compiler.validation().validateMemory(wasm, 16).get();
        System.out.println("Memory within 16 pages: " + memoryValid);

        System.out.println();
    }

    static void securityExample(SynorCompiler compiler) throws Exception {
        System.out.println("=== Security Scanning ===");

        byte[] wasm = createMinimalWasm();

        SecurityScanResult security = compiler.analysis().securityScan(wasm).get();

        System.out.println("Security score: " + security.getScore() + "/100");

        if (security.getIssues() != null && !security.getIssues().isEmpty()) {
            System.out.println("\nSecurity issues:");
            for (SecurityIssue issue : security.getIssues()) {
                String icon;
                switch (issue.getSeverity().toLowerCase()) {
                    case "critical": icon = "[CRIT]"; break;
                    case "high": icon = "[HIGH]"; break;
                    case "medium": icon = "[MED]"; break;
                    case "low": icon = "[LOW]"; break;
                    default: icon = "[???]";
                }
                System.out.println(icon + " [" + issue.getSeverity().toUpperCase() + "] " + issue.getType());
                System.out.println("   " + issue.getDescription());
                if (issue.getLocation() != null) {
                    System.out.println("   at " + issue.getLocation());
                }
            }
        } else {
            System.out.println("No security issues found!");
        }

        if (security.getRecommendations() != null && !security.getRecommendations().isEmpty()) {
            System.out.println("\nRecommendations:");
            for (String rec : security.getRecommendations()) {
                System.out.println("  • " + rec);
            }
        }

        System.out.println();
    }

    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02x", b));
        }
        return sb.toString();
    }
}
