using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using MegaRobo.Contract;

namespace MegaRobo.RRQuartz
{
    public abstract class JobItemBase
    {
        private string _name;

        public JobItemBase()
        {
        }

        public JobItemBase(JobItemBase jobItemBase)
        {
            this.Name = jobItemBase.Name;
            this.GroupName = jobItemBase.GroupName;
            this.RunFailedWithJobName = jobItemBase.RunFailedWithJobName;
            this.RunSuccessWithJobName = jobItemBase.RunSuccessWithJobName;
            this.TryCount = jobItemBase.TryCount;
            this.Descr = jobItemBase.Descr;
            this.Title = jobItemBase.Title;
            this.TargetService = jobItemBase.TargetService;
            this.CanExecute = jobItemBase.CanExecute;
            this.Data = jobItemBase.Data;
            this.Timeout = jobItemBase.Timeout;
            this.Guid = System.Guid.NewGuid().ToString("N");
        }

        public JobItemBase(string name, string title, IConnection targetService, string descr = null, [CallerFilePath] string sourceFilePath = "", [CallerMemberName] string memberName = "", [CallerLineNumber] int sourceLineNumber = 0)
        {
            this.Name = name;
            this.Title = title;
            this.TargetService = targetService;
            this.Descr = descr;
            this.ExecCount = 0;
            this.Sender = @$"member:{memberName}\line:{sourceLineNumber}\sender:{Path.GetFileName(sourceFilePath)}";
            this.Guid = System.Guid.NewGuid().ToString("N");
        }

        public JobItemBase(string name, string title, IConnection targetService, TimeSpan timeout, string descr = null, [CallerFilePath] string sourceFilePath = "", [CallerMemberName] string memberName = "", [CallerLineNumber] int sourceLineNumber = 0)
            : this(name, title, targetService, descr, sourceFilePath, memberName, sourceLineNumber)
        {
            this.Timeout = timeout;
        }

        public IConnection TargetService { get; protected set; }
        public string Sender { get; }
        public string GroupName { get; set; }
        public object Data { get; set; }

        public JobStatus Status { get; protected set; } = JobStatus.None;
        public Stopwatch UsedTime { get; set; } = new();
        public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(5);
        public int ExecCount { get; set; } = 0;

        public string RunSuccessWithJobName { get; set; }
        public string RunFailedWithJobName { get; set; }

        /// <summary>
        /// 重试次数.  -1:循环
        /// </summary>
        public int TryCount { get; set; } = 3;

        public string Name
        {
            get => this._name?.ToLower();
            protected set => this._name = value;
        }

        public string Title { get; set; }

        public string Guid { get; }

        public string Descr { get; set; }

        /// <summary>
        /// 剩余时间
        /// </summary>
        public double RemainderTime => Math.Max(0, (this.Timeout - this.UsedTime.Elapsed).TotalSeconds);

        public DateTime Crtime { get; protected set; } = DateTime.Now;

        public Func<bool> CanExecute { get; set; }

        public abstract void Restart();

        public abstract Task OnExecuteActionAsync();

        public void Stop()
        {
            this.UsedTime.Stop();
        }

        public void SetStatus(JobStatus status)
        {
            this.Status = status;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="timeOut">超时时间</param>
        /// <param name="mode">0-替换,1-追加</param>
        public void SetTimeout(TimeSpan timeOut, int mode)
        {
            this.Timeout = mode == 0 ? timeOut : this.Timeout.Add(timeOut);
        }

        public bool CheckTimeout() => this.UsedTime.Elapsed > this.Timeout;
    }

    public abstract class JobItemBase<TMode> : JobItemBase where TMode : Enum
    {
        public JobItemBase(TMode mode, string name, string title, IConnection targetService, TimeSpan timeout, string descr = null) : base(name, title, targetService, timeout, descr)
        {
            this.Mode = mode;
        }

        public TMode Mode { get; }
    }

    public enum JobStatus
    {
        None = 1,
        HasSend = 2,
        Ack = 11,
        RunFailed = 21,
        RunSuccess = 22
    }
}