using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.Hosting;
using Serilog;
using Serilog.Events;
using Serilog.Formatting.Json;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;

// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.

var builder = WebApplication.CreateBuilder(args);

Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Verbose()
    .Enrich.WithProperty("Id", "TEST")
    .Enrich.WithProperty("AppName", "[ALL YOUR BASE ARE BELONG TO US!]")
    .WriteTo.Async(a => a.Console())
    .WriteTo.Async(a => a.File(
        path: "logs/web-.log",
        rollingInterval: RollingInterval.Day,
        fileSizeLimitBytes: 1_000_000,
        rollOnFileSizeLimit: true,
        retainedFileCountLimit: 5,
        shared: true))
    .WriteTo.Async(a => a.File(
        new JsonFormatter(),
        path: "logs/test.json",
        rollingInterval: RollingInterval.Day))
    //.WriteTo.Seq("http://localhost:5341") // central logging
    .CreateLogger();

builder.Services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(Path.Combine(Directory.GetCurrentDirectory(), "keys")))
    .SetApplicationName("SerilogStressTest");

builder.Services.AddRazorPages();

builder.WebHost.ConfigureKestrel(options =>
{
    options.ListenAnyIP(6000);
});

var app = builder.Build();

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();

// Run tests
_ = Task.Run(RunLoggingTestsAsync);
_ = Task.Run(RunWebLoggingTestsAsync);
_ = Task.Run(RunLoggingExceptionTests);
_ = Task.Run(RunWebLoggingExceptionTests);
//_ = Task.Run(RunMemoryLeakTest);
_ = Task.Run(RunTcpLoggerTest);

app.Run();


async Task RunLoggingTestsAsync()
{
    for (var i = 0; i < 9_000_000; i++)
    {
        Log.Information("test to file {i} INFO", i);
        Log.Fatal("test to file {i} CRITICAL", i);
        Log.Debug("test to file {i} DEBUG", i);
        Log.Error("test to file {i} ERROR", i);
        Log.Verbose("test to file {i} TRACE", i);
        Log.Warning("test to file {i} WARNING", i);

        Console.WriteLine($"Logged: {i}");
        await Task.Delay(1);
    }
}

async Task RunWebLoggingTestsAsync()
{
    int i = 0;

    while (true)
    {
        i++;

        Log.Information("web-test {i}", i);
        Log.Debug("web-test {i}", i);
        Log.Warning("web-test {i}", i);
        Log.Error("web-test {i}", i);
        Log.Verbose("web-test {i}", i);

        await Task.Delay(1);
    }
}

void RunLoggingExceptionTests()
{
    for (int i = 0; i < 10; i++)
    {
        try
        {
            throw new Exception($"Normal Exception {i}");
        }
        catch (Exception ex)
        {
            Log.Error(ex, "Exception {Index}", i);
            Console.WriteLine($"Normal ExceptionLogged: {i}");
        }
    }
}

void RunWebLoggingExceptionTests()
{
    for (int i = 0; i < 10; i++)
    {
        try
        {
            throw new Exception($"WebException {i}");
        }
        catch (Exception ex)
        {
            Log.Fatal(ex, "CRITICAL");
            Log.Debug(ex, "DEBUG");
            Log.Error(ex, "ERROR");
            Log.Verbose(ex, "TRACE");
            Log.Warning(ex, "WARNING");
            Log.Information(ex, "INFORMATION");

            Console.WriteLine($"WebExceptionLogged: {i}");
        }
    }
}
async Task RunMemoryLeakTest()
{
    var managedLeak = new List<byte[]>();

    while (true)
    {
        managedLeak.Add(new byte[5_000_000]); // 5MB
        Marshal.AllocHGlobal(10_000_000);     // 10MB unmanaged

        await Task.Delay(500);
    }
}

async Task RunTcpLoggerTest()
{
    using var client = new TcpClient();

    try
    {
        await client.ConnectAsync("192.168.1.1", 12345);

        int i = 0;
        while (true)
        {
            var message = Encoding.UTF8.GetBytes($"TCP log {++i}\n");
            await client.GetStream().WriteAsync(message);
            await Task.Delay(1000);
        }
    }
    catch
    {
        Log.Warning("TCP server not reachable");
    }
}
