using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading;
using BITKit;
using Cysharp.Threading.Tasks;

namespace Net.Project.B.Dialogue
{
    /// <summary>
    /// 对话服务
    /// </summary>
    public interface IDialogueService
    {
        /// <summary>
        /// 所有对话
        /// </summary>
        IReadOnlyDictionary<int, IDialogueData> Dialogues { get; }

        /// <summary>
        /// 对话开始
        /// </summary>
        public event Func<IDialogueData, UniTask> OnDialogueStart;

        /// <summary>
        /// 对话结束
        /// </summary>
        public event Action<IDialogueData> OnDialogueEnd;

        /// <summary>
        /// 对话选择
        /// </summary>
        public event Func<IDialogueData, IReadOnlyCollection<IDialogueChoice>, UniTask<int>> OnDialogueChoose;

        /// <summary>
        /// 对话选择回调
        /// </summary>
        public event Action<IDialogueData, IDialogueChoice> OnDialogueChose;

        /// <summary>
        /// 开启对话
        /// </summary>
        /// <param name="dialogueData">对话</param>
        /// <returns></returns>
        UniTask CreateDialogue(IDialogueData dialogueData);

        UniTask<int> CreateDialogue(IDialogueData dialogueData, IReadOnlyCollection<IDialogueChoice> dialogueChoices);
    }

    public class DialogueService : IDialogueService
    {
        public IReadOnlyDictionary<int, IDialogueData> Dialogues => _dialogues;

        public event Func<IDialogueData, UniTask> OnDialogueStart;
        public event Action<IDialogueData> OnDialogueEnd;
        public event Func<IDialogueData, IReadOnlyCollection<IDialogueChoice>, UniTask<int>> OnDialogueChoose;
        public event Action<IDialogueData, IDialogueChoice> OnDialogueChose;
        public async UniTask CreateDialogue(IDialogueData dialogueData)
        {
            dialogueData.CancellationTokenSource ??= new CancellationTokenSource();

            if (_dialogues.TryAdd(dialogueData.Identity, dialogueData) is false)
            {
                dialogueData.Identity = _dialogues.Keys.Max() + 1;
                _dialogues.TryAdd(dialogueData.Identity, dialogueData);
            }

            try
            {
                await OnDialogueStart.UniTaskFunc(dialogueData)
                    .AttachExternalCancellation(dialogueData.CancellationTokenSource.Token);
            }
            catch (OperationCanceledException)
            {

            }
            catch (OperationAbortedException)
            {

            }
            finally
            {
                dialogueData.CancellationTokenSource.Cancel();
                
                OnDialogueEnd?.Invoke(dialogueData);
            }
        }

        public async UniTask<int> CreateDialogue(IDialogueData dialogueData,
            IReadOnlyCollection<IDialogueChoice> dialogueChoices)
        {
            if (OnDialogueChoose is null)
            {
                throw new NullReferenceException("OnDialogueChoose is null,unable to create dialogue");
            }

            var task = OnDialogueChoose.Invoke(dialogueData, dialogueChoices);
            var index = await task;

            OnDialogueChose?.Invoke(dialogueData, dialogueChoices.ElementAt(index));

            return index;
        }

        private readonly ConcurrentDictionary<int, IDialogueData> _dialogues = new();
    }
}