using System.Net;
using System.Net.Sockets;
using CS2B_shipping_company.Container;
using CS2B_shipping_company.Exception;

namespace CS2B_shipping_company;

public class Server
{
    private readonly Company company;
    private TcpListener listener;
    private bool serverRunning;
    
    public Server(Company company)
    {
        this.company = company;
    }
    
    public void SetupServer(int port = 23)
    {
        listener = new TcpListener(IPAddress.Any, port);
        listener.Start();
        serverRunning = true;
    }
    
    public async Task ServerLoop()
    {
        while (serverRunning)
        {
            try
            {
                TcpClient client = await listener.AcceptTcpClientAsync();
                NetworkStream stream = client.GetStream();
                StreamReader reader = new StreamReader(stream);
                StreamWriter writer = new StreamWriter(stream) { AutoFlush = true };
                {
                    await HandleClient(reader, writer);
                }
            }
            catch (System.Exception ex)
            {
                Console.WriteLine($"Error: {ex.Message}");
            }
        }
    }
    
    private async Task HandleClient(StreamReader reader, StreamWriter writer)
    {
        try
        {
            await writer.WriteLineAsync("WELCOME");
            
            string? command = await reader.ReadLineAsync();
            if (command == "STOP")
            {
                await writer.WriteLineAsync("ACK");
                return;
            }
            
            if (command != "START")
            {
                await writer.WriteLineAsync("ERR;Invalid command");
                return;
            }
            
            await writer.WriteLineAsync("TYPE");
            string? type = await reader.ReadLineAsync();
            
            BaseContainer container = null;
            
            switch (type?.ToUpper())
            {
                case "FULL":
                    container = await ProcessFullContainer(reader, writer);
                    break;
                case "HALF":
                    container = await ProcessHalfContainer(reader, writer);
                    break;
                case "QUART":
                    container = new QuarterContainer("Unknown", "Unknown");
                    break;
                default:
                    await writer.WriteLineAsync("ERR;Invalid Type");
                    return;
            }
            
            if (container != null)
            {
                company.Add(container);
                await writer.WriteLineAsync("ACK");
                await writer.WriteLineAsync(company.GenerateReport());
            }
        }
        catch (ShippingException ex)
        {
            await writer.WriteLineAsync($"ERR;{ex.Message}");
        }
        catch (System.Exception ex)
        {
            await writer.WriteLineAsync($"ERR;{ex.Message}");
        }
    }
    
    private async Task<FullContainer> ProcessFullContainer(StreamReader reader, StreamWriter writer)
    {
        await writer.WriteLineAsync("FRIDGE");
        string? fridgeResponse = await reader.ReadLineAsync();
        bool isRefrigerated = fridgeResponse?.ToUpper() == "YES";
        
        await writer.WriteLineAsync("WEIGHT");
        if (!int.TryParse(await reader.ReadLineAsync(), out int weight))
            throw new InvalidInputException("Invalid weight");
        
        FullContainer container = new FullContainer("Unknown", "Unknown")
        {
            IsRefrigerated = isRefrigerated,
            Weight = weight
        };
        
        return container;
    }
    
    private async Task<HalfContainer> ProcessHalfContainer(StreamReader reader, StreamWriter writer)
    {
        await writer.WriteLineAsync("VOLUME");
        if (!int.TryParse(await reader.ReadLineAsync(), out int volume))
            throw new InvalidInputException("Invalid volume");
        
        HalfContainer container = new HalfContainer("Unknown", "Unknown")
        {
            Volume = volume
        };
        
        return container;
    }
    
    public void StopServer()
    {
        serverRunning = false;
        listener?.Stop();
    }
}
