using System;
using Microsoft.Extensions.Options;
using Sfs2X;
using Sfs2X.Core;
using Sfs2X.Entities;
using Sfs2X.Entities.Data;
using Sfs2X.Entities.Variables;
using Sfs2X.Requests;
using Sfs2X.Util;
using sodoff.Configuration;

namespace sodoff.Services;

public class MMOClientService : IHostedService
{
    private readonly IOptions<ApiServerConfig> Config;
    private ConfigData SFSConfig;

    public SmartFox SFSClient { get; private set; }
    public bool IsLoggedIn { get; private set; }
    public User? CurrentUser { get; private set; }

    public MMOClientService(IOptions<ApiServerConfig> config)
    {
        Config = config;

        // set SFSConfig
        SFSConfig = new ConfigData
        {
            Host = Config.Value.MMOAdress,
            Port = Config.Value.MMOPort,
            Zone = "JumpStart"
        };

        // set SFSClient
        SFSClient = new SmartFox
        {
            ThreadSafeMode = false // this is set to true by default which requires Unity's thread processing stuff
        };
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        // connect and login to mmo server using SFSConfig

        Console.WriteLine("Starting Connection To MMO Server...");

        SFSClient.Connect(SFSConfig);

        SFSClient.AddEventListener(SFSEvent.CONNECTION, OnConnectionEstablished);
        SFSClient.AddEventListener(SFSEvent.CONNECTION_LOST, OnConnectionLost);
        SFSClient.AddEventListener(SFSEvent.LOGIN, OnLogin);

        // return completed task (SmartFox seems to be completely synchronous)
        return Task.CompletedTask;
    }


    public Task SetRoomVarForRoom(int roomId, RoomVariable var)
    {
        if (IsLoggedIn)
        {
            Room room = SFSClient.GetRoomById(roomId);
            if (room != null) room.SetVariable(var);
            else return Task.CompletedTask;
        }
        else throw new InvalidOperationException("MMO Client Was Not Ready");

        return Task.CompletedTask;
    }

    public Task SendCommandToUser(string userId, string cmd, string[] parameters)
    {
        // SFSClient doesn't seem to have a method to send commands to specific users, so we'll make a custom extension endpoint that will handle it
        ISFSObject obj = SFSObject.NewInstance();
        obj.PutUtfString("UID", userId);
        obj.PutUtfString("CMD", cmd);
        obj.PutUtfStringArray("ARR", parameters);

        SFSClient.Send(new ExtensionRequest("SUE", obj));

        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        Console.WriteLine("Stopping Connection To MMO Server...");

        if(SFSClient.IsConnected)
        {
            if(IsLoggedIn) SFSClient.Send(new LogoutRequest());
            SFSClient.Disconnect();
        }

        return Task.CompletedTask;
    }

    private void OnConnectionEstablished(BaseEvent evt)
    {
        bool success = (bool)evt.Params["success"];
        if (success)
        {
            Console.WriteLine("Connection Established. Sending Login Request...");
            SFSClient.Send(new LoginRequest("API"));
        }
    }

    private void OnConnectionLost(BaseEvent evt)
    {
        Console.WriteLine($"Connection To The MMO Server Was Lost. Reason - {(string)evt.Params["reason"]}");
        Console.WriteLine("MMO Service Currently Not Ready.\nThis Requires A Full API Restart For Realtime Communication To Be Reestablished.");
    }

    private void OnLogin(BaseEvent evt)
    {
        CurrentUser = (User)evt.Params["user"];
        IsLoggedIn = CurrentUser != null;
        Console.WriteLine($"Logged In? - {IsLoggedIn}");
        if (IsLoggedIn) Console.WriteLine("MMO Service Ready.");
        else Console.WriteLine("MMO Service Not Ready.");
    }
}
