using Flurl;
using InnovEnergy.API.DataModel;
using InnovEnergy.Lib.WebServer;
using InnovEnergy.Lib.Utils;
using Newtonsoft.Json;
using static System.Text.Encoding;
using static InnovEnergy.Lib.WebServer.Default;

namespace InnovEnergy.API;

public static class Program
{
    public static String RootDir { get; private set; } = "/home/eef/sync/work/Code/innovenergy/server/WebMonitoring/src";

    public static Data Data { get; set; }

    public static Dictionary<String, LoginState> LoggedInUsers { get; } = new Dictionary<String, LoginState>();


    public static void Main(String[] args)
    {
        RootDir = args.FirstOrDefault() ?? RootDir;

        Console.WriteLine("start");

#pragma warning disable 4014
        SyncWithVrm();
#pragma warning restore 4014

        Data = Data.Load();
        WebServer.ServeOnLocalHost(8080, Serve);

        Console.WriteLine("stop");
    }

    private static async Task SyncWithVrm()
    {
        Console.WriteLine("Starting VRM sync");

        var vrmLink = await VrmLink.Login();

        while (true)
        {
            try
            {
                var data = Data.Load();
                var json = data.SerializeJson();
                await vrmLink.SyncWithVrm(data);

                var changed = json != data.SerializeJson();

                if (changed)
                {
                    data.Save("Synced from VRM", "system");
                    var edit = data.Edit;
                    Console.WriteLine($"{edit.TimeStamp} [{edit.Version}] {edit.Action}");
                    Data = data;
                }

                await Task.Delay(TimeSpan.FromMinutes(10));
            }
            catch (Exception e)
            {
                Console.WriteLine("Failed to sync with VRM:\n" + e);
            }

        }
    }


    public static HttpResponse Serve(HttpRequest request)
    {
        var pathSegments = request.Url.PathSegments;

        var p0 = pathSegments.ElementAtOrDefault(0);

        Console.WriteLine(p0);

        return p0 switch
        {
            "api" => ServeApi(request),
            _ => ServeAssets(request)
        };
    }

    private static HttpResponse Login(QueryParamCollection query)
    {
        var userName = query.FirstOrDefault("username") as String;
        var password = query.FirstOrDefault("password") as String;

        // TODO: password check

        if (String.IsNullOrEmpty(userName))
            return HttpForbidden;

        var userPath   = Data.Root.FindDescendantUser(userName);
        var user       = userPath?.User();
        var homeFolder = userPath?.Parent.Folder();

        if (user is null || homeFolder is null)
            return HttpForbidden;

        var loginState = new LoginState(user, homeFolder, Data.Edit.Version);

        LoggedInUsers[loginState.Token] = loginState; // TODO: remove stale tokens

        return CreateJsonResponse(loginState);
    }


    private static HttpResponse ServeApi(HttpRequest request)
    {
        var url    = request.Url;
        var query  = url.QueryParams;
        var action = query.FirstOrDefault("action") as String;

        Console.WriteLine(action);
            
        if (action == "login")
            return Login(query);

        if (query.FirstOrDefault("token") is not String token || 
            !LoggedInUsers.TryGetValue(token, out var loginState))
            return HttpForbidden;

        var result = action switch
        {
            "createUser"         => Api.CreateUser(query, loginState),
            "editUser"           => Api.EditUser(query, loginState),
            "renameInstallation" => Api.RenameInstallation(query, loginState),

            // "deleteUser"         => Api.DeleteUser(query, loginState),
            // "deleteFolder"       => Api.DeleteFolder(query, loginState),
            // "deleteInstallation" => Api.DeleteInstallation(query, loginState),
            //"rename"             => Api.Delete(query, loginState),

            _ => Result.Failure("unsupported api call")
        };



        return result
            .WriteToDisk(loginState.LoginUser)
            .CreateResponse(loginState);
    }




    private static HttpResponse ServeAssets(HttpRequest request)
    {
        var localPath = RootDir + request.Url.Path;

        if (request.Url.PathSegments.Count == 0)
            localPath = RootDir + "/index.html";

        Console.WriteLine(request.Url + " => " + localPath + " | " + InferContentType.FromExtension(localPath));

        if (!File.Exists(localPath))
        {
            Console.WriteLine("404");
            return HttpNotFound;
        }

        return new HttpResponse
        {
            Content = File.ReadAllBytes(localPath), // TODO: cache?
            ContentType = InferContentType.FromExtension(localPath)
        };
    }


    private static Result WriteToDisk(this Result result, User loggedInUser)
    {
        if (result.Succeeded)
        {
            Data.Save(result.Message, loggedInUser.Name);
        }

        return result;
    }

    private static HttpResponse CreateResponse(this Result result, LoginState loginState)
    {
        loginState.DataVersion = Data.Edit.Version; // important!
        loginState.Error = result.Succeeded ? null : result.Message;

        return CreateJsonResponse(loginState);
    }

    private static HttpResponse CreateJsonResponse<T>(T payload)
    {
        var json = JsonConvert.SerializeObject(payload, Data.WireFormatJsonSettings);

        return new HttpResponse
        {
            Content = json.Apply(UTF8.GetBytes),
            ContentType = ContentType.ApplicationJson,
            Headers = new[] { new HttpHeader("Access-Control-Allow-Origin", "*") }
        };
    }
}