using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;

namespace Synor.Compiler;

/// <summary>
/// Synor Compiler SDK for C#/.NET
///
/// Smart contract compilation, optimization, and ABI generation.
/// </summary>
public class SynorCompiler : IDisposable
{
    private readonly CompilerConfig _config;
    private readonly HttpClient _httpClient;
    private readonly JsonSerializerOptions _jsonOptions;
    private bool _closed;

    public ContractsClient Contracts { get; }
    public AbiClient Abi { get; }
    public AnalysisClient Analysis { get; }
    public ValidationClient Validation { get; }

    public SynorCompiler(CompilerConfig config)
    {
        _config = config;
        _httpClient = new HttpClient
        {
            BaseAddress = new Uri(config.Endpoint),
            Timeout = TimeSpan.FromMilliseconds(config.Timeout)
        };
        _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {config.ApiKey}");
        _httpClient.DefaultRequestHeaders.Add("X-SDK-Version", "csharp/0.1.0");

        _jsonOptions = new JsonSerializerOptions
        {
            PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
            Converters = { new JsonStringEnumConverter(JsonNamingPolicy.SnakeCaseLower) }
        };

        Contracts = new ContractsClient(this);
        Abi = new AbiClient(this);
        Analysis = new AnalysisClient(this);
        Validation = new ValidationClient(this);
    }

    public OptimizationLevel DefaultOptimizationLevel => _config.DefaultOptimizationLevel;

    public async Task<bool> HealthCheckAsync(CancellationToken ct = default)
    {
        try
        {
            var result = await GetAsync<Dictionary<string, object>>("/health", ct);
            return result.TryGetValue("status", out var status) && status?.ToString() == "healthy";
        }
        catch
        {
            return false;
        }
    }

    public Task<Dictionary<string, object>> GetInfoAsync(CancellationToken ct = default) =>
        GetAsync<Dictionary<string, object>>("/info", ct);

    public void Close()
    {
        _closed = true;
        _httpClient.Dispose();
    }

    public bool IsClosed => _closed;

    public void Dispose()
    {
        Close();
        GC.SuppressFinalize(this);
    }

    public async Task<CompilationResult> CompileAsync(
        byte[] wasm,
        CompilationRequest? request = null,
        CancellationToken ct = default)
    {
        var wasmBase64 = Convert.ToBase64String(wasm);
        var body = new Dictionary<string, object>
        {
            ["wasm"] = wasmBase64,
            ["optimization_level"] = (request?.OptimizationLevel ?? _config.DefaultOptimizationLevel).ToApiString(),
            ["use_wasm_opt"] = request?.UseWasmOpt ?? _config.UseWasmOpt,
            ["validate"] = request?.Validate ?? _config.Validate,
            ["extract_metadata"] = request?.ExtractMetadata ?? _config.ExtractMetadata,
            ["generate_abi"] = request?.GenerateAbi ?? _config.GenerateAbi
        };

        if (request?.StripOptions != null)
        {
            body["strip_options"] = request.StripOptions;
        }

        return await PostAsync<CompilationResult>("/compile", body, ct);
    }

    internal async Task<T> GetAsync<T>(string path, CancellationToken ct = default)
    {
        CheckClosed();
        var response = await _httpClient.GetAsync(path, ct);
        return await HandleResponseAsync<T>(response);
    }

    internal async Task<T> PostAsync<T>(string path, object? body = null, CancellationToken ct = default)
    {
        CheckClosed();
        var content = body != null
            ? JsonContent.Create(body, options: _jsonOptions)
            : JsonContent.Create(new { });
        var response = await _httpClient.PostAsync(path, content, ct);
        return await HandleResponseAsync<T>(response);
    }

    private async Task<T> HandleResponseAsync<T>(HttpResponseMessage response)
    {
        var json = await response.Content.ReadAsStringAsync();
        if (!response.IsSuccessStatusCode)
        {
            var error = JsonSerializer.Deserialize<Dictionary<string, object>>(json, _jsonOptions) ?? new();
            throw new CompilerException(
                error.TryGetValue("message", out var msg) ? msg?.ToString() ?? $"HTTP {(int)response.StatusCode}" : $"HTTP {(int)response.StatusCode}",
                error.TryGetValue("code", out var code) ? code?.ToString() : null,
                (int)response.StatusCode
            );
        }
        return JsonSerializer.Deserialize<T>(json, _jsonOptions)!;
    }

    private void CheckClosed()
    {
        if (_closed) throw new CompilerException("Client has been closed", "CLIENT_CLOSED");
    }
}

/// <summary>Contracts sub-client</summary>
public class ContractsClient
{
    private readonly SynorCompiler _compiler;

    internal ContractsClient(SynorCompiler compiler) => _compiler = compiler;

    public Task<CompilationResult> CompileAsync(byte[] wasm, CompilationRequest? request = null, CancellationToken ct = default) =>
        _compiler.CompileAsync(wasm, request, ct);

    public Task<CompilationResult> CompileDevAsync(byte[] wasm, CancellationToken ct = default) =>
        _compiler.CompileAsync(wasm, new CompilationRequest
        {
            OptimizationLevel = OptimizationLevel.None,
            UseWasmOpt = false,
            Validate = true
        }, ct);

    public Task<CompilationResult> CompileProductionAsync(byte[] wasm, CancellationToken ct = default) =>
        _compiler.CompileAsync(wasm, new CompilationRequest
        {
            OptimizationLevel = OptimizationLevel.Aggressive,
            UseWasmOpt = true,
            Validate = true,
            ExtractMetadata = true,
            GenerateAbi = true
        }, ct);

    public Task<CompilationResult> GetAsync(string contractId, CancellationToken ct = default) =>
        _compiler.GetAsync<CompilationResult>($"/contracts/{contractId}", ct);

    public async Task<List<CompilationResult>> ListAsync(int? limit = null, int? offset = null, CancellationToken ct = default)
    {
        var path = "/contracts";
        var qs = new List<string>();
        if (limit.HasValue) qs.Add($"limit={limit}");
        if (offset.HasValue) qs.Add($"offset={offset}");
        if (qs.Count > 0) path += "?" + string.Join("&", qs);

        var result = await _compiler.GetAsync<ContractsListResponse>(path, ct);
        return result.Contracts ?? new List<CompilationResult>();
    }

    public async Task<byte[]> GetCodeAsync(string contractId, CancellationToken ct = default)
    {
        var result = await _compiler.GetAsync<CodeResponse>($"/contracts/{contractId}/code", ct);
        return Convert.FromBase64String(result.Code);
    }
}

/// <summary>ABI sub-client</summary>
public class AbiClient
{
    private readonly SynorCompiler _compiler;

    internal AbiClient(SynorCompiler compiler) => _compiler = compiler;

    public async Task<ContractAbi> ExtractAsync(byte[] wasm, CancellationToken ct = default)
    {
        var wasmBase64 = Convert.ToBase64String(wasm);
        return await _compiler.PostAsync<ContractAbi>("/abi/extract", new { wasm = wasmBase64 }, ct);
    }

    public Task<ContractAbi> GetAsync(string contractId, CancellationToken ct = default) =>
        _compiler.GetAsync<ContractAbi>($"/contracts/{contractId}/abi", ct);

    public async Task<string> EncodeCallAsync(FunctionAbi functionAbi, object[] args, CancellationToken ct = default)
    {
        var result = await _compiler.PostAsync<DataResponse>("/abi/encode", new
        {
            function = functionAbi,
            args
        }, ct);
        return result.Data;
    }

    public async Task<object[]> DecodeResultAsync(FunctionAbi functionAbi, string data, CancellationToken ct = default)
    {
        var result = await _compiler.PostAsync<ValuesResponse>("/abi/decode", new
        {
            function = functionAbi,
            data
        }, ct);
        return result.Values ?? Array.Empty<object>();
    }
}

/// <summary>Analysis sub-client</summary>
public class AnalysisClient
{
    private readonly SynorCompiler _compiler;

    internal AnalysisClient(SynorCompiler compiler) => _compiler = compiler;

    public async Task<ContractAnalysis> AnalyzeAsync(byte[] wasm, CancellationToken ct = default)
    {
        var wasmBase64 = Convert.ToBase64String(wasm);
        return await _compiler.PostAsync<ContractAnalysis>("/analysis", new { wasm = wasmBase64 }, ct);
    }

    public Task<ContractAnalysis> GetAsync(string contractId, CancellationToken ct = default) =>
        _compiler.GetAsync<ContractAnalysis>($"/contracts/{contractId}/analysis", ct);

    public async Task<ContractMetadata> ExtractMetadataAsync(byte[] wasm, CancellationToken ct = default)
    {
        var wasmBase64 = Convert.ToBase64String(wasm);
        return await _compiler.PostAsync<ContractMetadata>("/analysis/metadata", new { wasm = wasmBase64 }, ct);
    }

    public async Task<long> EstimateDeployGasAsync(byte[] wasm, CancellationToken ct = default)
    {
        var wasmBase64 = Convert.ToBase64String(wasm);
        var result = await _compiler.PostAsync<GasResponse>("/analysis/estimate-gas", new { wasm = wasmBase64 }, ct);
        return result.Gas;
    }

    public async Task<SecurityAnalysis> SecurityScanAsync(byte[] wasm, CancellationToken ct = default)
    {
        var wasmBase64 = Convert.ToBase64String(wasm);
        var result = await _compiler.PostAsync<SecurityResponse>("/analysis/security", new { wasm = wasmBase64 }, ct);
        return result.Security!;
    }
}

/// <summary>Validation sub-client</summary>
public class ValidationClient
{
    private readonly SynorCompiler _compiler;

    internal ValidationClient(SynorCompiler compiler) => _compiler = compiler;

    public async Task<ValidationResult> ValidateAsync(byte[] wasm, CancellationToken ct = default)
    {
        var wasmBase64 = Convert.ToBase64String(wasm);
        return await _compiler.PostAsync<ValidationResult>("/validate", new { wasm = wasmBase64 }, ct);
    }

    public async Task<bool> IsValidAsync(byte[] wasm, CancellationToken ct = default)
    {
        var result = await ValidateAsync(wasm, ct);
        return result.Valid;
    }

    public async Task<List<string>> GetErrorsAsync(byte[] wasm, CancellationToken ct = default)
    {
        var result = await ValidateAsync(wasm, ct);
        return result.Errors?.ConvertAll(e => e.Message) ?? new List<string>();
    }

    public async Task<bool> ValidateExportsAsync(byte[] wasm, List<string> requiredExports, CancellationToken ct = default)
    {
        var wasmBase64 = Convert.ToBase64String(wasm);
        var result = await _compiler.PostAsync<ValidResponse>("/validate/exports", new
        {
            wasm = wasmBase64,
            required_exports = requiredExports
        }, ct);
        return result.Valid;
    }

    public async Task<bool> ValidateMemoryAsync(byte[] wasm, int maxPages, CancellationToken ct = default)
    {
        var wasmBase64 = Convert.ToBase64String(wasm);
        var result = await _compiler.PostAsync<ValidResponse>("/validate/memory", new
        {
            wasm = wasmBase64,
            max_pages = maxPages
        }, ct);
        return result.Valid;
    }
}

// Response helper types
internal record ContractsListResponse(List<CompilationResult>? Contracts);
internal record CodeResponse(string Code);
internal record DataResponse(string Data);
internal record ValuesResponse(object[]? Values);
internal record GasResponse(long Gas);
internal record SecurityResponse(SecurityAnalysis? Security);
internal record ValidResponse(bool Valid);
