using System;
using System.IO;
using System.Net;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Threading;
using System.Text;
using Newtonsoft.Json;
using System.Reflection;
using System.Linq;
using HeyRed.Mime;

namespace Tesses.Http
{

    
    public abstract class ASPEndpointRequestHandler : IRequestHandler
    {
        protected virtual Stream GetPostFileStream(string name,string filename,string contentType)
        {
            return new MemoryStream();
        }
        
        List<___RouteMethod> ___RouteMethods=new List<___RouteMethod>();

        private class ___RouteMethod
        {
            public string Method;
            public bool ContainsServerCtx;
            public bool ContainsMultipart;
            public bool ReturnsVoid;

            public string Name;

            public MethodInfo info;

            public ASPEndpointRequestHandler _instance;
            

            public void Call(ServerContext ctx,MultipartParser p,Dictionary<string,List<string>> args)
            {
               
                if(!ContainsMultipart && p != null)
                {
                    foreach(var item in p.Parse(_instance.GetPostFileStream,true))
                    {
                        if(!item.HasFileName)
                        {
                            args.Add(item.Name,item.GetStringData());
                        }
                    }
                }
                object[] ar_= _args.Select<___Arg,object>((e)=>{
                    return e.GetArgument(ctx,p,args);
                }).ToArray();

                
                object o=info.Invoke(_instance,ar_);
                if(!ReturnsVoid)
                {
                    IResponse r = o as IResponse;
                    Stream s = o as Stream;
                    string r2 = o as string;
                    StringBuilder b=o as StringBuilder;
                    

                    if(r!=null)
                    {
                        r.Handle(ctx);
                    }else if(s != null)
                    {
                        FileResponse resp=new FileResponse(s,"application/octet-stream",true);
                        resp.Handle(ctx);
                    }else if(b != null)
                    {
                        TextResponse resp=new TextResponse(b.ToString(),"text/plain");
                        resp.Handle(ctx);
                    }
                    else if(!string.IsNullOrEmpty(r2))
                    {
                        TextResponse resp=new TextResponse(r2,"text/plain");
                        resp.Handle(ctx);
                    }else{
                        TextResponse resp=new TextResponse(JsonConvert.SerializeObject(o),"application/json");
                        resp.Handle(ctx);
                    }

                    
                }
            }

            public List<___Arg> _args=new List<___Arg>();
        }
        private class ___Arg
        {
      
            public bool _isServerCtx;
            public bool _isMultipart;
            public string name;
            public Type type;
            private object Default()
            {
                if(type.GetTypeInfo().IsValueType)
                {
                    return Activator.CreateInstance(type);
                }
                return null;
               
            }
        private object ___to_type_ar(Type t,List<string> items)
        {
            object o;
            if(t.IsArray)
            {
                var t0=t.MakeArrayType();
                object[] items2=new object[items.Count]; 
                foreach(var item in items)
                {
                    ___to_type(t0,item);
                }
                o=items;
            }else{
                o=___to_type(t,items.FirstOrDefault());
            }
            return o;
        }
        private object ___to_type(Type t,string value)
        {
            try{
            if(t == typeof(String))
            {
                return value;
            }
            foreach(var item in t.GetMethods())
            {
                var _params = item.GetParameters();
                if(_params.Length != 1)
                {
                    continue;
                }
                if(_params[0].ParameterType != typeof(String)) continue;

                if(item.IsStatic && !item.IsConstructor && item.Name == "Parse")
                {
                    return item.Invoke(null,new object[]{value});
                }
                if(item.IsConstructor)
                {
                    return Activator.CreateInstance(t,value);
                }

            }
            
            MethodInfo method = typeof(JsonConvert).GetMethod(nameof(JsonConvert.SerializeObject));
            MethodInfo generic = method.MakeGenericMethod(t);
            return generic.Invoke(null,new object[]{value});
            }catch(Exception ex)
            {
                _=ex;
               return  Default();
            }
        }
            public object GetArgument(ServerContext ctx,MultipartParser p,Dictionary<string,List<string>> args)
            {
                if(_isServerCtx)    
                {
                    return ctx;
                }
                if(_isMultipart)
                {
                    return p;
                }

                

                foreach(var item in args)
                {
                    if(item.Key.ToLower() == name)
                    {
                        return ___to_type_ar(type,item.Value);
                    }
                }
                
                return Default();
                

            }
        }


        public TextResponse String(string text,string mimeType="text/html")
        {
            
            return new TextResponse(text,mimeType);
        }

        public FileResponse File(Stream strm,string mimeType,bool ownStream=true)
        {
            return new FileResponse(strm,mimeType,ownStream);
        }

        public FileResponse File(string filename,bool inline=true)
        {
            return new FileResponse(filename,inline);
        }
        public FileResponse File(Stream strm,string mimeType,string filename,bool inline=false,bool ownStream=false)
        {
            return new FileResponse(strm,mimeType,filename,inline,ownStream);
        }


        bool ___has_loaded=false;
     
 
        


        private void ___lazy_load_everything()
        {
            if(!___has_loaded)
            {
                Type t=GetType();
                foreach(var item in t.GetMethods())
                {
                    IHttpRoute route = new HttpGetAttribute($"/{item.Name}");

                    foreach(var r in item.GetCustomAttributes())
                    {
                        var rte=r as IHttpRoute;
                        if(rte != null)
                        {
                            route=rte;
                            if(string.IsNullOrWhiteSpace(rte.RouteName))
                            {
                                
                                route.RouteName = $"/{item.Name}";
                            }
                            
                            break;
                        }
                    }
                    
                    ___RouteMethod m=new ___RouteMethod();
                    var __props = item.GetParameters();
                    m.ContainsServerCtx = __props.Any(e=>e.ParameterType == typeof(ServerContext));
                    m.ContainsMultipart = __props.Any(e=>e.ParameterType == typeof(MultipartParser));
                    m.ReturnsVoid = item.ReturnType == typeof(void);
                    m.info=item;
                    m.Name = route.RouteName;
                    m.Method=route.Method;
                    m._instance = this;
                    m._args=new List<___Arg>();
                    
                    if(m.ContainsServerCtx || !m.ReturnsVoid)
                    {
                        //add route to 
                        foreach(var args in __props)
                        {
                            ___Arg a=new ___Arg();
                            a._isServerCtx = args.ParameterType == typeof(ServerContext);
                            a._isMultipart = args.ParameterType == typeof(MultipartParser);
                            a.name = args.Name.ToLower();
                            a.type = args.ParameterType;
                            m._args.Add(a);
                        }
                        ___RouteMethods.Add(m);

                    }
                }
            }
            ___has_loaded=true;
        }
        public async Task Handle(ServerContext ctx)
        {
            ___lazy_load_everything();
            Dictionary<string,List<string>> args=new Dictionary<string, List<string>>();
            MultipartParser parser=null;
            foreach(var m in ___RouteMethods)
            {
                args.Clear();
                string p=ctx.Request.GetQueryParameters(ctx.Request.CurrentUrl,args);

                if(m.Method == ctx.Request.RequestLine.Method && m.Name ==p)
                {
                    //want to check for method

                    
                    if(m.Method == "POST")
                    {
                        string _ctt;
                        if(ctx.Request.Headers.TryGetFirst("Content-Type",out _ctt))
                        {
                            if(_ctt.StartsWith("multipart/form-data"))
                            {
                                parser = ctx.Request.GetMultipartParser();
                            }
                            if(_ctt == "application/x-www-form-urlencoded")
                            {
                                ctx.Request.GetUrlEncodedPost(args);
                            }
                        }
                    }

                    m.Call(ctx,parser,args);

                    break;
                }
            }
            
            await Task.CompletedTask;
        }
    }

   
    public class FileResponse : IResponse
    {
        bool inline;
        public FileResponse(Stream strm,string mimeType,bool ownStream)
        {
            this.ownStream=ownStream;
            Stream = strm;
            MimeType=mimeType;
            FileName="";
        }
        public FileResponse(Stream strm,string mimeType,string filename,bool inline,bool ownStream)
        {
            this.ownStream=ownStream;
            Stream =strm;
            MimeType = mimeType;
            FileName=filename;
            this.inline=inline;
        }
        bool ownStream;
        public FileResponse(string filename,bool inline)
        {
            ownStream=true;
            MimeType=MimeTypesMap.GetMimeType(filename);
            Stream=File.OpenRead(filename);
            FileName = Path.GetFileName(filename);
            this.inline=inline;
        }
        public Stream Stream {get;set;}
        public string MimeType {get;set;}

        public string FileName {get;set;}

        public void Handle(ServerContext ctx)
        {
            if(string.IsNullOrWhiteSpace(FileName))
            {
                ctx.Response.WithContentType(MimeType).SendRangableResponseStream(Stream);

            }else{
                 ctx.Response.WithContentType(MimeType).WithFileName(FileName,inline).SendRangableResponseStream(Stream);
                
            }
            if(ownStream)
            Stream.Dispose();
        }
    }

    public class TextResponse  : IResponse
    {
        public TextResponse(string text,string mime)
        {
            Text=text;
            MimeType=mime;
        }
        public string Text {get;set;}
        public string MimeType {get;set;}

        public void Handle(ServerContext ctx)
        {
            ctx.Response.WithContentType(MimeType).SendText(Text);
        }
    }

    public interface IResponse
    {
        void Handle(ServerContext ctx);
    }
 
    public interface IHttpRoute
    {
        string RouteName {get;set;}

        string Method {get;}
    }

    [AttributeUsage(AttributeTargets.Method)]

    public sealed class HttpGetAttribute :Attribute, IHttpRoute
    {
        public string Method {get{return "GET";}}
        public string RouteName {get;set;}
        public HttpGetAttribute(string name)
        {
            RouteName=name;
        }
        public HttpGetAttribute()
        {
            RouteName = "";
        }
    }
     [AttributeUsage(AttributeTargets.Method)]

    public sealed class HttpPostAttribute :Attribute, IHttpRoute
    {
        public string Method {get{return "POST";}}
        public string RouteName {get;set;}
        public HttpPostAttribute(string name)
        {
            RouteName=name;
        }
        public HttpPostAttribute()
        {
            RouteName = "";
        }
    }

    public sealed class HttpMethodAttribute : Attribute, IHttpRoute
    {
        string _meth;
        public string Method {get{return _meth;}}

        public string RouteName {get;set;}

        public HttpMethodAttribute(string method)
        {
            _meth=method;
            RouteName="";
        }
        public HttpMethodAttribute(string method,string route)
        {
            _meth = method;
            RouteName = route;
        }
    }

   
}