using System;
using Ooui.AspNetCore;

namespace Microsoft.AspNetCore.Builder
{
    public static class OouiMiddlewareExtensions
    {
        public static void UseOoui (this IApplicationBuilder app, string jsPath = "/ooui.js", string webSocketPath = "/ooui.ws", TimeSpan? sessionTimeout = null)
        {
            if (string.IsNullOrWhiteSpace (webSocketPath))
                throw new ArgumentException ("A path to be used for Ooui web sockets must be specified", nameof (webSocketPath));
            
            if (string.IsNullOrWhiteSpace (jsPath))
                throw new ArgumentException ("A path to be used for Ooui JavaScript must be specified", nameof (jsPath));

            WebSocketHandler.WebSocketPath = webSocketPath;

            if (sessionTimeout.HasValue) {
                WebSocketHandler.SessionTimeout = sessionTimeout.Value;
            }

            var webSocketOptions = new WebSocketOptions () {
                KeepAliveInterval = WebSocketHandler.SessionTimeout,
                ReceiveBufferSize = 4 * 1024
            };
            app.UseWebSockets (webSocketOptions);

            Ooui.UI.ServerEnabled = false;

            app.Use (async (context, next) =>
            {
                var response = context.Response;

                if (context.Request.Path == jsPath) {
                    var clientJsBytes = Ooui.UI.ClientJsBytes;
                    var clientJsEtag = Ooui.UI.ClientJsEtag;
                    if (context.Request.Headers.TryGetValue ("If-None-Match", out var inms) && inms.Count > 0 && inms[0] == clientJsEtag) {
                        response.StatusCode = 304;
                    }
                    else {
                        response.StatusCode = 200;
                        response.ContentLength = clientJsBytes.Length;
                        response.ContentType = "application/javascript; charset=utf-8";
                        response.Headers.Add ("Cache-Control", "public, max-age=60");
                        response.Headers.Add ("Etag", clientJsEtag);
                        using (var s = response.Body) {
                            await s.WriteAsync (clientJsBytes, 0, clientJsBytes.Length).ConfigureAwait (false);
                        }
                    }
                }
                else if (context.Request.Path == WebSocketHandler.WebSocketPath) {
                    if (context.WebSockets.IsWebSocketRequest) {
                        await WebSocketHandler.HandleWebSocketRequestAsync (context).ConfigureAwait (false);
                    }
                    else {
                        context.Response.StatusCode = 400;
                    }
                }
                else if (Ooui.UI.TryGetFileContentAtPath (context.Request.Path, out var file)) {
                    if (context.Request.Headers.TryGetValue ("If-None-Match", out var inms) && inms.Count > 0 && inms[0] == file.Etag) {
                        response.StatusCode = 304;
                    }
                    else {
                        response.StatusCode = 200;
                        response.ContentLength = file.Content.Length;
                        response.ContentType = file.ContentType;
                        response.Headers.Add ("Cache-Control", "public, max-age=60");
                        response.Headers.Add ("Etag", file.Etag);
                        using (var s = response.Body) {
                            await s.WriteAsync (file.Content, 0, file.Content.Length).ConfigureAwait (false);
                        }
                    }
                }
                else {
                    await next ().ConfigureAwait (false);
                }
            });
        }
    }
}
