/// Synor Compiler SDK Client
///
/// Smart contract compilation, optimization, and ABI generation.
library synor_compiler_client;

import 'dart:convert';
import 'package:http/http.dart' as http;

import 'types.dart';

export 'types.dart';

/// Synor Compiler SDK Client.
///
/// Smart contract compilation, optimization, and ABI generation.
///
/// Example:
/// ```dart
/// final compiler = SynorCompiler(CompilerConfig(apiKey: 'your-api-key'));
///
/// final result = await compiler.compile(wasmBytes);
/// print('Optimized size: ${result.optimizedSize} bytes');
/// ```
class SynorCompiler {
  final CompilerConfig _config;
  final http.Client _httpClient;
  bool _closed = false;

  late final ContractsClient contracts;
  late final AbiClient abi;
  late final AnalysisClient analysis;
  late final ValidationClient validation;

  SynorCompiler(this._config, {http.Client? httpClient})
      : _httpClient = httpClient ?? http.Client() {
    contracts = ContractsClient(this);
    abi = AbiClient(this);
    analysis = AnalysisClient(this);
    validation = ValidationClient(this);
  }

  /// Get the default optimization level.
  OptimizationLevel get defaultOptimizationLevel => _config.optimizationLevel;

  /// Check service health.
  Future<bool> healthCheck() async {
    try {
      final result = await _get('/health');
      return result['status'] == 'healthy';
    } catch (_) {
      return false;
    }
  }

  /// Get service info.
  Future<Map<String, dynamic>> getInfo() async {
    return await _get('/info');
  }

  /// Close the client.
  void close() {
    _closed = true;
    _httpClient.close();
  }

  /// Check if client is closed.
  bool get isClosed => _closed;

  /// Compile WASM bytecode.
  Future<CompilationResult> compile(
    List<int> wasm, {
    OptimizationLevel? optimizationLevel,
    StripOptions? stripOptions,
    bool? useWasmOpt,
    bool? validate,
    bool? extractMetadata,
    bool? generateAbi,
  }) async {
    final wasmBase64 = base64Encode(wasm);

    final body = {
      'wasm': wasmBase64,
      'optimization_level':
          (optimizationLevel ?? _config.optimizationLevel).toApiString(),
      'strip_options':
          (stripOptions ?? _config.stripOptions ?? const StripOptions())
              .toJson(),
      'use_wasm_opt': useWasmOpt ?? _config.useWasmOpt,
      'validate': validate ?? _config.validate,
      'extract_metadata': extractMetadata ?? _config.extractMetadata,
      'generate_abi': generateAbi ?? _config.generateAbi,
    };

    final result = await _post('/compile', body);
    return CompilationResult.fromJson(result);
  }

  Future<Map<String, dynamic>> _get(String path) async {
    _checkClosed();

    final response = await _httpClient.get(
      Uri.parse('${_config.endpoint}$path'),
      headers: _headers,
    );

    return _handleResponse(response);
  }

  Future<Map<String, dynamic>> _post(
      String path, Map<String, dynamic> body) async {
    _checkClosed();

    final response = await _httpClient.post(
      Uri.parse('${_config.endpoint}$path'),
      headers: _headers,
      body: jsonEncode(body),
    );

    return _handleResponse(response);
  }

  Map<String, String> get _headers => {
        'Authorization': 'Bearer ${_config.apiKey}',
        'Content-Type': 'application/json',
        'X-SDK-Version': 'flutter/0.1.0',
      };

  Map<String, dynamic> _handleResponse(http.Response response) {
    final body = response.body;

    if (response.statusCode >= 400) {
      Map<String, dynamic> error = {};
      try {
        error = jsonDecode(body);
      } catch (_) {
        error = {'message': body.isEmpty ? 'HTTP ${response.statusCode}' : body};
      }
      throw CompilerError(
        error['message'] ?? 'HTTP ${response.statusCode}',
        code: error['code'],
        httpStatus: response.statusCode,
      );
    }

    return jsonDecode(body);
  }

  void _checkClosed() {
    if (_closed) {
      throw const CompilerError('Client has been closed', code: 'CLIENT_CLOSED');
    }
  }
}

/// Contracts sub-client.
class ContractsClient {
  final SynorCompiler _compiler;

  ContractsClient(this._compiler);

  /// Compile WASM bytecode with default settings.
  Future<CompilationResult> compile(
    List<int> wasm, {
    OptimizationLevel? optimizationLevel,
    StripOptions? stripOptions,
    bool? useWasmOpt,
    bool? validate,
    bool? extractMetadata,
    bool? generateAbi,
  }) {
    return _compiler.compile(
      wasm,
      optimizationLevel: optimizationLevel,
      stripOptions: stripOptions,
      useWasmOpt: useWasmOpt,
      validate: validate,
      extractMetadata: extractMetadata,
      generateAbi: generateAbi,
    );
  }

  /// Compile with development settings.
  Future<CompilationResult> compileDev(List<int> wasm) {
    return _compiler.compile(
      wasm,
      optimizationLevel: OptimizationLevel.none,
      useWasmOpt: false,
      validate: true,
    );
  }

  /// Compile with production settings.
  Future<CompilationResult> compileProduction(List<int> wasm) {
    return _compiler.compile(
      wasm,
      optimizationLevel: OptimizationLevel.aggressive,
      useWasmOpt: true,
      validate: true,
      extractMetadata: true,
      generateAbi: true,
    );
  }

  /// Get a compiled contract by ID.
  Future<CompilationResult> get(String contractId) async {
    final result = await _compiler._get('/contracts/$contractId');
    return CompilationResult.fromJson(result);
  }

  /// List compiled contracts.
  Future<List<CompilationResult>> list({int? limit, int? offset}) async {
    var path = '/contracts';
    final params = <String>[];
    if (limit != null) params.add('limit=$limit');
    if (offset != null) params.add('offset=$offset');
    if (params.isNotEmpty) path += '?${params.join('&')}';

    final result = await _compiler._get(path);
    return (result['contracts'] as List<dynamic>?)
            ?.map((c) => CompilationResult.fromJson(c))
            .toList() ??
        [];
  }

  /// Get optimized bytecode for a contract.
  Future<List<int>> getCode(String contractId) async {
    final result = await _compiler._get('/contracts/$contractId/code');
    return base64Decode(result['code'] ?? '');
  }
}

/// ABI sub-client.
class AbiClient {
  final SynorCompiler _compiler;

  AbiClient(this._compiler);

  /// Extract ABI from WASM bytecode.
  Future<ContractAbi> extract(List<int> wasm) async {
    final wasmBase64 = base64Encode(wasm);
    final result =
        await _compiler._post('/abi/extract', {'wasm': wasmBase64});
    return ContractAbi.fromJson(result);
  }

  /// Get ABI for a compiled contract.
  Future<ContractAbi> get(String contractId) async {
    final result = await _compiler._get('/contracts/$contractId/abi');
    return ContractAbi.fromJson(result);
  }

  /// Encode function call.
  Future<String> encodeCall(FunctionAbi functionAbi, List<dynamic> args) async {
    final result = await _compiler._post('/abi/encode', {
      'function': {
        'name': functionAbi.name,
        'selector': functionAbi.selector,
        'inputs': functionAbi.inputs
            .map((i) => {'name': i.name, 'type': {'kind': i.type.kind}})
            .toList(),
        'outputs': functionAbi.outputs
            .map((o) => {'name': o.name, 'type': {'kind': o.type.kind}})
            .toList(),
      },
      'args': args,
    });
    return result['data'] ?? '';
  }

  /// Decode function result.
  Future<List<dynamic>> decodeResult(
      FunctionAbi functionAbi, String data) async {
    final result = await _compiler._post('/abi/decode', {
      'function': {
        'name': functionAbi.name,
        'selector': functionAbi.selector,
        'inputs': functionAbi.inputs
            .map((i) => {'name': i.name, 'type': {'kind': i.type.kind}})
            .toList(),
        'outputs': functionAbi.outputs
            .map((o) => {'name': o.name, 'type': {'kind': o.type.kind}})
            .toList(),
      },
      'data': data,
    });
    return result['values'] ?? [];
  }
}

/// Analysis sub-client.
class AnalysisClient {
  final SynorCompiler _compiler;

  AnalysisClient(this._compiler);

  /// Analyze WASM bytecode.
  Future<ContractAnalysis> analyze(List<int> wasm) async {
    final wasmBase64 = base64Encode(wasm);
    final result =
        await _compiler._post('/analysis', {'wasm': wasmBase64});
    return ContractAnalysis.fromJson(result);
  }

  /// Get analysis for a compiled contract.
  Future<ContractAnalysis> get(String contractId) async {
    final result = await _compiler._get('/contracts/$contractId/analysis');
    return ContractAnalysis.fromJson(result);
  }

  /// Extract metadata from WASM bytecode.
  Future<ContractMetadata> extractMetadata(List<int> wasm) async {
    final wasmBase64 = base64Encode(wasm);
    final result =
        await _compiler._post('/analysis/metadata', {'wasm': wasmBase64});
    return ContractMetadata.fromJson(result);
  }

  /// Extract source map from WASM bytecode.
  Future<SourceMap?> extractSourceMap(List<int> wasm) async {
    final wasmBase64 = base64Encode(wasm);
    final result =
        await _compiler._post('/analysis/source-map', {'wasm': wasmBase64});
    final sourceMap = result['source_map'];
    return sourceMap != null ? SourceMap.fromJson(sourceMap) : null;
  }

  /// Estimate gas for contract deployment.
  Future<int> estimateDeployGas(List<int> wasm) async {
    final wasmBase64 = base64Encode(wasm);
    final result =
        await _compiler._post('/analysis/estimate-gas', {'wasm': wasmBase64});
    return result['gas'] ?? 0;
  }

  /// Get security analysis.
  Future<SecurityAnalysis> securityScan(List<int> wasm) async {
    final wasmBase64 = base64Encode(wasm);
    final result =
        await _compiler._post('/analysis/security', {'wasm': wasmBase64});
    return SecurityAnalysis.fromJson(result['security'] ?? {});
  }
}

/// Validation sub-client.
class ValidationClient {
  final SynorCompiler _compiler;

  ValidationClient(this._compiler);

  /// Validate WASM bytecode against VM requirements.
  Future<ValidationResult> validate(List<int> wasm) async {
    final wasmBase64 = base64Encode(wasm);
    final result =
        await _compiler._post('/validate', {'wasm': wasmBase64});
    return ValidationResult.fromJson(result);
  }

  /// Check if WASM is valid.
  Future<bool> isValid(List<int> wasm) async {
    final result = await validate(wasm);
    return result.valid;
  }

  /// Get validation errors.
  Future<List<String>> getErrors(List<int> wasm) async {
    final result = await validate(wasm);
    return result.errors.map((e) => e.message).toList();
  }

  /// Validate contract exports.
  Future<bool> validateExports(
      List<int> wasm, List<String> requiredExports) async {
    final wasmBase64 = base64Encode(wasm);
    final result = await _compiler._post('/validate/exports', {
      'wasm': wasmBase64,
      'required_exports': requiredExports,
    });
    return result['valid'] ?? false;
  }

  /// Validate memory limits.
  Future<bool> validateMemory(List<int> wasm, int maxPages) async {
    final wasmBase64 = base64Encode(wasm);
    final result = await _compiler._post('/validate/memory', {
      'wasm': wasmBase64,
      'max_pages': maxPages,
    });
    return result['valid'] ?? false;
  }
}
