﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Bops.Service.Middlewares
{
    using Microsoft.Owin;
    using Microsoft.Owin.FileSystems;
    using System.IO;

    public class BundleMiddleware : OwinMiddleware
    {
        string routePrefix;
        IFileSystem fileSystem;
        bool cacheBundlesInMemory;

        Dictionary<string, String[]> bundleCache;

        public BundleMiddleware(OwinMiddleware next)
            : this(next, new BundleConfig() { FileSystem = new PhysicalFileSystem(".") })
        { }

        public BundleMiddleware(OwinMiddleware next, BundleConfig config)
            : base (next)
        {
            bundleCache = new Dictionary<string, string[]>();

            fileSystem = config.FileSystem;
            routePrefix = config.RoutePrefix;
            cacheBundlesInMemory = config.CacheBundlesInMemory;

        }
        public override async Task Invoke(IOwinContext context)
        {
            await tryParse(context);
            await Next.Invoke(context);
        }

        private async Task tryParse(IOwinContext context)
        {
            // TODO: suporte a cache do navegador
            if (!context.Request.Path.HasValue || context.Request.Path.Value.Length == 0)
                return;
            string requestPath = context.Request.Path.Value.Substring(1); // Removendo a barra
            if (requestPath.Length <= routePrefix.Length || !requestPath.StartsWith(routePrefix, StringComparison.InvariantCultureIgnoreCase))
                return;

            requestPath = requestPath.Substring(routePrefix.Length);

            string mimeType;
            try
            {
                var content = getBundle(requestPath, out mimeType);
                await context.Response.WriteAsync(content);
                context.Response.ContentType = mimeType;
                return;
            }
            catch (FileNotFoundException)
            {
                return;
            }
            catch (Exception ex)
            {
                Log.Error(ex);
                return;
            }
        }

        private string getBundle(string bundleName, out string mimeType)
        {
            if (cacheBundlesInMemory && bundleCache.ContainsKey(bundleName.ToLower()))
            {
                string[] value = bundleCache[bundleName];
                mimeType = value[0];
                return value[1];
            }

            string extension = Path.GetExtension(bundleName);
            string indexFilePath = Path.ChangeExtension(bundleName, extension + ".index");
            IFileInfo indexFileInfo;
            string content;
            mimeType = getMimeType(extension);
            if (fileSystem.TryGetFileInfo(indexFilePath, out indexFileInfo))
            {
                StringBuilder str = new StringBuilder();
                List<string> files = new List<string>();
                string parentPath = indexFilePath.Remove(indexFilePath.LastIndexOf('/'));
                using (Stream stream = indexFileInfo.CreateReadStream())
                using (StreamReader reader = new StreamReader(stream))
                {
                    while (!reader.EndOfStream)
                    {
                        string line = reader.ReadLine().Trim();
                        if (!line.StartsWith("//")) // Linhas comentadas
                        {
                            files.Add(line);
                        }
                    }
                }
                foreach (string partFile in files)
                {
                    IFileInfo partFileInfo;
                    string partFilePath = Path.Combine(parentPath, partFile);
                    if (fileSystem.TryGetFileInfo(partFilePath, out partFileInfo))
                    {
                        using (Stream stream = partFileInfo.CreateReadStream())
                        using (StreamReader reader = new StreamReader(stream))
                        {
                            str.AppendLine(reader.ReadToEnd());
                        }
                    }
                    else
                    {
                        throw new IndexedFileNotFoundException(indexFilePath, partFilePath);
                    }
                }
                content = str.ToString();
                if (cacheBundlesInMemory)
                {
                    bundleCache.Add(bundleName.ToLower(), new string[] { mimeType, content });
                }
                return content;
            }
            else
            {
                throw new FileNotFoundException("Not found.", bundleName);
            }
        }

        private string getMimeType(string extension)
        {
            switch (extension)
            {
                case ".js":
                    return "text/javascript";
                case ".css":
                    return "text/css";
                default:
                    return "application/octet-stream";
            }
        }
    }
}
