using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using LittleShop.Client.Extensions;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Serilog;
using TeleBotClient;

namespace TeleBotClient
{
    public class SimulatorProgram
    {
        public static async Task Main(string[] args)
        {
            // Configure Serilog
            Log.Logger = new LoggerConfiguration()
                .MinimumLevel.Debug()
                .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}")
                .WriteTo.File("logs/simulator-.txt", rollingInterval: RollingInterval.Day)
                .CreateLogger();

            try
            {
                // Build configuration
                var configuration = new ConfigurationBuilder()
                    .SetBasePath(Directory.GetCurrentDirectory())
                    .AddJsonFile("appsettings.json", optional: false)
                    .AddEnvironmentVariables()
                    .Build();

                // Configure services
                var services = new ServiceCollection();
                
                // Add logging
                services.AddLogging(builder =>
                {
                    builder.ClearProviders();
                    builder.AddSerilog();
                });

                // Add LittleShop client
                services.AddLittleShopClient(options =>
                {
                    options.BaseUrl = configuration["LittleShop:ApiUrl"] ?? "https://localhost:5001";
                    options.TimeoutSeconds = 30;
                    options.MaxRetryAttempts = 3;
                });

                // Add simulator
                services.AddTransient<BotSimulator>();
                services.AddTransient<TestRunner>();

                var serviceProvider = services.BuildServiceProvider();

                // Run tests
                var runner = serviceProvider.GetRequiredService<TestRunner>();
                await runner.RunAsync();
            }
            catch (Exception ex)
            {
                Log.Fatal(ex, "Application terminated unexpectedly");
            }
            finally
            {
                Log.CloseAndFlush();
            }
        }
    }

    public class TestRunner
    {
        private readonly BotSimulator _simulator;
        private readonly ILogger<TestRunner> _logger;
        private readonly List<SimulationResult> _allResults = new();

        public TestRunner(BotSimulator simulator, ILogger<TestRunner> logger)
        {
            _simulator = simulator;
            _logger = logger;
        }

        public async Task RunAsync()
        {
            _logger.LogInformation("===========================================");
            _logger.LogInformation("🚀 TeleBot Client Simulator");
            _logger.LogInformation("===========================================");
            
            while (true)
            {
                Console.WriteLine("\n📋 Select an option:");
                Console.WriteLine("1. Run single simulation");
                Console.WriteLine("2. Run multiple simulations");
                Console.WriteLine("3. Run stress test");
                Console.WriteLine("4. View statistics");
                Console.WriteLine("5. Exit");
                Console.Write("\nChoice: ");
                
                var choice = Console.ReadLine();
                
                switch (choice)
                {
                    case "1":
                        await RunSingleSimulation();
                        break;
                        
                    case "2":
                        await RunMultipleSimulations();
                        break;
                        
                    case "3":
                        await RunStressTest();
                        break;
                        
                    case "4":
                        DisplayStatistics();
                        break;
                        
                    case "5":
                        _logger.LogInformation("Exiting simulator...");
                        return;
                        
                    default:
                        Console.WriteLine("Invalid choice. Please try again.");
                        break;
                }
            }
        }

        private async Task RunSingleSimulation()
        {
            _logger.LogInformation("\n🎯 Running single simulation...\n");
            
            var result = await _simulator.SimulateUserSession();
            _allResults.Add(result);
            
            DisplaySimulationResult(result);
        }

        private async Task RunMultipleSimulations()
        {
            Console.Write("\nHow many simulations to run? ");
            if (!int.TryParse(Console.ReadLine(), out var count) || count <= 0)
            {
                Console.WriteLine("Invalid number.");
                return;
            }
            
            _logger.LogInformation("\n🎯 Running {Count} simulations...\n", count);
            
            var results = new List<SimulationResult>();
            var successful = 0;
            var failed = 0;
            
            for (int i = 1; i <= count; i++)
            {
                _logger.LogInformation("▶️ Simulation {Number}/{Total}", i, count);
                
                var result = await _simulator.SimulateUserSession();
                results.Add(result);
                _allResults.Add(result);
                
                if (result.Success)
                    successful++;
                else
                    failed++;
                
                // Brief pause between simulations
                await Task.Delay(1000);
            }
            
            DisplayBatchSummary(results, successful, failed);
        }

        private async Task RunStressTest()
        {
            Console.Write("\nNumber of concurrent simulations: ");
            if (!int.TryParse(Console.ReadLine(), out var concurrent) || concurrent <= 0)
            {
                Console.WriteLine("Invalid number.");
                return;
            }
            
            Console.Write("Total simulations to run: ");
            if (!int.TryParse(Console.ReadLine(), out var total) || total <= 0)
            {
                Console.WriteLine("Invalid number.");
                return;
            }
            
            _logger.LogInformation("\n⚡ Starting stress test: {Concurrent} concurrent, {Total} total\n", 
                concurrent, total);
            
            var semaphore = new SemaphoreSlim(concurrent);
            var tasks = new List<Task<SimulationResult>>();
            var startTime = DateTime.UtcNow;
            
            for (int i = 0; i < total; i++)
            {
                var task = Task.Run(async () =>
                {
                    await semaphore.WaitAsync();
                    try
                    {
                        return await _simulator.SimulateUserSession();
                    }
                    finally
                    {
                        semaphore.Release();
                    }
                });
                tasks.Add(task);
            }
            
            var allResults = await Task.WhenAll(tasks);
            var results = allResults.ToList();
            _allResults.AddRange(results);
            
            var duration = DateTime.UtcNow - startTime;
            
            DisplayStressTestResults(results, duration, concurrent, total);
        }

        private void DisplayStatistics()
        {
            if (!_allResults.Any())
            {
                Console.WriteLine("\n📊 No simulation data available yet.");
                return;
            }
            
            Console.WriteLine("\n📊 Session Statistics:");
            Console.WriteLine($"   Total Simulations: {_allResults.Count}");
            Console.WriteLine($"   Successful: {_allResults.Count(r => r.Success)}");
            Console.WriteLine($"   Failed: {_allResults.Count(r => !r.Success)}");
            Console.WriteLine($"   Success Rate: {(_allResults.Count(r => r.Success) * 100.0 / _allResults.Count):F1}%");
            
            var successful = _allResults.Where(r => r.Success).ToList();
            if (successful.Any())
            {
                Console.WriteLine($"\n💰 Order Statistics:");
                Console.WriteLine($"   Total Orders: {successful.Count}");
                Console.WriteLine($"   Total Revenue: ${successful.Sum(r => r.OrderTotal):F2}");
                Console.WriteLine($"   Average Order: ${successful.Average(r => r.OrderTotal):F2}");
                Console.WriteLine($"   Min Order: ${successful.Min(r => r.OrderTotal):F2}");
                Console.WriteLine($"   Max Order: ${successful.Max(r => r.OrderTotal):F2}");
                
                // Payment distribution
                var payments = successful
                    .Where(r => !string.IsNullOrEmpty(r.PaymentCurrency))
                    .GroupBy(r => r.PaymentCurrency)
                    .Select(g => new { Currency = g.Key, Count = g.Count() })
                    .OrderByDescending(x => x.Count);
                
                Console.WriteLine($"\n💳 Payment Methods:");
                foreach (var p in payments)
                {
                    Console.WriteLine($"   {p.Currency}: {p.Count} ({p.Count * 100.0 / successful.Count:F1}%)");
                }
            }
        }

        private void DisplaySimulationResult(SimulationResult result)
        {
            Console.WriteLine("\n========================================");
            Console.WriteLine($"📋 Simulation Result: {result.SessionId}");
            Console.WriteLine("========================================");
            
            if (result.Success)
            {
                Console.WriteLine("✅ Status: SUCCESS");
            }
            else
            {
                Console.WriteLine($"❌ Status: FAILED - {result.ErrorMessage}");
            }
            
            Console.WriteLine($"⏱️ Duration: {result.Duration.TotalSeconds:F2}s");
            
            if (result.Steps.Any())
            {
                Console.WriteLine("\n📝 Steps Completed:");
                foreach (var step in result.Steps)
                {
                    Console.WriteLine($"   {step}");
                }
            }
            
            if (result.Cart != null && result.Cart.Items.Any())
            {
                Console.WriteLine($"\n🛒 Shopping Cart ({result.Cart.Items.Count} items):");
                foreach (var item in result.Cart.Items)
                {
                    Console.WriteLine($"   - {item.Quantity}x {item.ProductName} @ ${item.UnitPrice:F2} = ${item.TotalPrice:F2}");
                }
                Console.WriteLine($"   Total: ${result.Cart.TotalAmount:F2}");
            }
            
            if (result.OrderId.HasValue)
            {
                Console.WriteLine($"\n📝 Order Details:");
                Console.WriteLine($"   Order ID: {result.OrderId}");
                Console.WriteLine($"   Total: ${result.OrderTotal:F2}");
            }
            
            if (!string.IsNullOrEmpty(result.PaymentCurrency))
            {
                Console.WriteLine($"\n💰 Payment Details:");
                Console.WriteLine($"   Currency: {result.PaymentCurrency}");
                Console.WriteLine($"   Amount: {result.PaymentAmount}");
            }
            
            Console.WriteLine("\n========================================");
        }

        private void DisplayBatchSummary(List<SimulationResult> results, int successful, int failed)
        {
            Console.WriteLine("\n📊 Batch Summary:");
            Console.WriteLine($"✅ Successful: {successful}");
            Console.WriteLine($"❌ Failed: {failed}");
            Console.WriteLine($"📈 Success Rate: {(successful * 100.0 / results.Count):F1}%");
            
            if (results.Any(r => r.Success))
            {
                var successfulResults = results.Where(r => r.Success).ToList();
                Console.WriteLine($"\n💰 Order Statistics:");
                Console.WriteLine($"   Average Order: ${successfulResults.Average(r => r.OrderTotal):F2}");
                Console.WriteLine($"   Total Revenue: ${successfulResults.Sum(r => r.OrderTotal):F2}");
                Console.WriteLine($"   Average Duration: {successfulResults.Average(r => r.Duration.TotalSeconds):F1}s");
            }
        }

        private void DisplayStressTestResults(List<SimulationResult> results, TimeSpan duration, int concurrent, int total)
        {
            var successful = results.Count(r => r.Success);
            var failed = results.Count(r => !r.Success);
            
            Console.WriteLine("\n📊 Stress Test Results:");
            Console.WriteLine($"⏱️ Total Duration: {duration.TotalSeconds:F1}s");
            Console.WriteLine($"✅ Successful: {successful}");
            Console.WriteLine($"❌ Failed: {failed}");
            Console.WriteLine($"📈 Success Rate: {(successful * 100.0 / total):F1}%");
            Console.WriteLine($"⚡ Throughput: {(total / duration.TotalSeconds):F2} simulations/second");
            Console.WriteLine($"🔄 Concurrency: {concurrent} simultaneous connections");
            
            if (failed > 0)
            {
                Console.WriteLine("\n❌ Failure Analysis:");
                var errors = results
                    .Where(r => !r.Success && !string.IsNullOrEmpty(r.ErrorMessage))
                    .GroupBy(r => r.ErrorMessage)
                    .Select(g => new { Error = g.Key, Count = g.Count() })
                    .OrderByDescending(x => x.Count)
                    .Take(5);
                
                foreach (var error in errors)
                {
                    Console.WriteLine($"   {error.Error}: {error.Count}");
                }
            }
        }
    }
}