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
{/// <summary>
    /// Check username and password are correct or if request can be anonymous
    /// </summary>
    /// <param name="username">Username, can and will be "" on first request for resource</param>
    /// <param name="password">Password, can and will be "" on first request for resource</param>
    /// <returns>true for authorized, false for unauthorized</returns>
    public delegate bool Authenticate(string username, string password);
    /// <summary>
    /// Check username and password are correct or if request can be anonymous
    /// </summary>
    /// <param name="context">Server Context</param>
    /// <param name="username">Username, can and will be "" on first request for resource</param>
    /// <param name="password">Password, can and will be "" on first request for resource</param>
    /// <returns>true for authorized, false for unauthorized</returns>
    public delegate bool AuthenticateWithContext(ServerContext context,string username,string password);
    /// <summary>
    /// Protect server with password
    /// </summary>
    public class BasicAuthRequestHandler : IRequestHandler
    {
        /// <summary>
        /// Construct server for user authorization
        /// </summary>
        /// <param name="auth">callback for authorization</param>
        /// <param name="inner">server to protect</param>
        /// <param name="realm">realm parameter in WWW-Auhenticate Header</param>
        public BasicAuthRequestHandler(Authenticate auth,IRequestHandler inner,string realm="SampleRealm")
        {
            Authenticate = auth;
            InnerServer = inner;
            Realm = realm;
        }
         /// <summary>
        /// Construct server for user authorization (With ServerContext in callback)
        /// </summary>
        /// <param name="auth">callback for authorization</param>
        /// <param name="inner">server to protect</param>
        /// <param name="realm">realm parameter in WWW-Auhenticate Header</param>
        public BasicAuthRequestHandler(AuthenticateWithContext auth,IRequestHandler inner,string realm = "SampleRealm")
        {
            AuthenticateWithContext=auth;
            InnerServer=inner;
            Realm = realm;
        }
        public async Task Handle(ServerContext ctx)
        {
            if(await Authorize(ctx))
            {
               
                    await RequestHandler.Guaranteed(ctx,InnerServer).Handle(ctx);

            }
            
        }
       
        /// <summary>
        /// Server to protect
        /// </summary>
        
        public IRequestHandler InnerServer { get; set; }
        /// <summary>
        /// Authentication callback without ServerContext
        /// </summary>
        public Authenticate Authenticate { get; set; }
        /// <summary>
        /// Authentication callback with ServerContext
        /// </summary>
    
        public AuthenticateWithContext AuthenticateWithContext {get;set;}
        /// <summary>
        /// Realm parameter in WWW-Authenticate header
        /// </summary>
        public string Realm { get; set; }
       
        private bool ValidAuth(ServerContext ctx)
        {
            string auth;
            if(Authenticate == null && AuthenticateWithContext == null) return true;
            if (ctx.Request.Headers.TryGetFirst("Authorization", out auth))
            {
                string[] authorization = auth.Split(' ');
                //authorization_basic

                if (authorization[0] == "Basic")
                {
                    string[] userPass = Encoding.UTF8.GetString(Convert.FromBase64String(authorization[1])).Split(new char[] { ':' },2);
                    //return userPass.Equals($"{config.UserName}:{config.Password}", StringComparison.Ordinal);
                    if(Authenticate != null)
                        return Authenticate(userPass[0], userPass[1]); 

                    if(AuthenticateWithContext != null)
                        return AuthenticateWithContext(ctx,userPass[0],userPass[2]);

                   
                }
            }else{
                  if(Authenticate != null)
                        return Authenticate("", ""); 

                    if(AuthenticateWithContext != null)
                        return AuthenticateWithContext(ctx,"","");

            }
            return false;
        }
        private async Task<bool> Authorize(ServerContext ctx)
        {
            if (Authenticate == null && AuthenticateWithContext == null)
                return true;

            if (ValidAuth(ctx))
                return true;

            ctx.Response.Headers.Add("WWW-Authenticate", $"Basic realm=\"{Realm}\"");
            ctx.Response.StatusLine = 401;
            await UnauthorizedPage(ctx);
            return false;
        }
        protected virtual async Task UnauthorizedPage(ServerContext ctx)
        {
            ctx.Response.SendStatusCodeHtml();
            await Task.CompletedTask;
        }
    }
}