using System;
using System.Diagnostics;
using System.Threading.Tasks;

namespace Tapeti.Flow.Default
{
    internal class FlowContext : IDisposable
    {
        private readonly IFlowHandlerContext? handlerContext;
        private IFlowStateLock? flowStateLock;
        private FlowState? flowState;


        public IFlowHandlerContext HandlerContext => handlerContext ?? throw new InvalidOperationException("FlowContext does not have a HandlerContext");
        public IFlowStateLock FlowStateLock => flowStateLock ?? throw new InvalidOperationException("FlowContext does not have a FlowStateLock");
        public FlowState FlowState => flowState ?? throw new InvalidOperationException("FlowContext does not have a FlowState");

        public bool HasFlowStateAndLock => flowState != null && flowStateLock != null;

        public Guid ContinuationID { get; set; }
        public ContinuationMetadata? ContinuationMetadata { get; set; }

        private int storeCalled;
        private int deleteCalled;


        public FlowContext(IFlowHandlerContext handlerContext, FlowState flowState, IFlowStateLock flowStateLock)
        {
            this.flowState = flowState;
            this.flowStateLock = flowStateLock;
            this.handlerContext = handlerContext;
        }


        public FlowContext(IFlowHandlerContext handlerContext)
        {
            this.handlerContext = handlerContext;
        }


        public void SetFlowState(FlowState newFlowState, IFlowStateLock newFlowStateLock)
        {
            flowState = newFlowState;
            flowStateLock = newFlowStateLock;
        }


        public ValueTask Store(bool persistent)
        {
            storeCalled++;

            FlowState.Data = Newtonsoft.Json.JsonConvert.SerializeObject(HandlerContext.Controller);
            return FlowStateLock.StoreFlowState(FlowState, persistent);
        }

        public ValueTask Delete()
        {
            deleteCalled++;
            return flowStateLock?.DeleteFlowState() ?? default;
        }

        public bool IsStoredOrDeleted()
        {
            return storeCalled > 0 || deleteCalled > 0;
        }

        public void EnsureStoreOrDeleteIsCalled()
        {
            if (!IsStoredOrDeleted())
                throw new InvalidProgramException("Neither Store nor Delete are called for the state of the current flow. FlowID = " + flowStateLock?.FlowID);

            Debug.Assert(storeCalled <= 1, "Store called more than once!");
            Debug.Assert(deleteCalled <= 1, "Delete called more than once!");
        }

        public void Dispose()
        {
            flowStateLock?.Dispose();
        }
    }
}
