using System;
using System.Threading;

using UnityEditor;

using Codice.Client.Common.EventTracking;
using Codice.CM.Common;
using Codice.LogWrapper;
using PlasticGui;
using PlasticGui.Help.Conditions;
using PlasticGui.WebApi.Responses;
using Unity.PlasticSCM.Editor.AssetUtils;
using Unity.PlasticSCM.Editor.Configuration;
using Unity.PlasticSCM.Editor.UI;
using Unity.PlasticSCM.Editor.Views.CreateWorkspace;
using Unity.PlasticSCM.Editor.WebApi;

namespace Unity.PlasticSCM.Editor.Hub.Operations
{
    internal class CreateWorkspace
    {
        internal static bool IsOperationInProgress { get { return mIsOperationInProgress; } }

        internal static void LaunchOperation(OperationParams parameters)
        {
            mIsOperationInProgress = true;

            mLog.Debug("Waiting for editor to be idle");

            Execute.AfterEditorIsIdleForSeconds(IDLE_DELAY_SECONDS, () =>
            {
                mLog.Debug("Editor is idle, proceeding with workspace creation");

                CreateWorkspace createWorkspaceOperation = new CreateWorkspace();
                createWorkspaceOperation.CreateWorkspaceOperation(parameters);
            });
        }

        internal static WorkspaceInfo CreateWorkspaceForRepSpec(
            RepositorySpec repositorySpec,
            string wkPath,
            ILog log)
        {
            CreateWorkspaceDialogUserAssistant assistant =
                CreateWorkspaceDialogUserAssistant.ForWkPathAndName(
                    PlasticGuiConfig.Get().Configuration.DefaultWorkspaceRoot,
                    PlasticGui.Plastic.API.GetAllWorkspacesArray());

            assistant.RepositoryChanged(
                repositorySpec.ToString(),
                string.Empty,
                string.Empty);

            WorkspaceInfo wkInfo = PlasticGui.Plastic.API.CreateWorkspace(
                wkPath,
                assistant.GetProposedWorkspaceName(),
                repositorySpec.ToString());

            log.DebugFormat("Created workspace '{0}' on '{1}'",
                wkInfo.Name,
                wkInfo.ClientPath);

            return wkInfo;
        }

        void CreateWorkspaceOperation(OperationParams parameters)
        {
            RefreshAsset.BeforeLongAssetOperation();

            try
            {
                ThreadPool.QueueUserWorkItem(
                    CreateWorkspaceIfNeeded,
                    parameters);

                while (mStatus != Status.Finished)
                {
                    if (mDisplayProgress)
                    {
                        DisplayProgress(mStatus, parameters.Repository);
                    }

                    Thread.Sleep(150);
                }
            }
            finally
            {
                mIsOperationInProgress = false;

                EditorUtility.ClearProgressBar();

                RefreshAsset.AfterLongAssetOperation(null);

                if (!mOperationFailed)
                {
                    UVCSPlugin.Instance.Enable();

                    UVCSWindow window = ShowWindow.UVCS();
                    window.ExecuteFullReload();

                    if (mCreatedWkInfo != null)
                        GetWindowIfOpened.BranchExplorer()?.OnWorkspaceCreated(
                            mCreatedWkInfo);
                }
            }
        }

        void CreateWorkspaceIfNeeded(object state)
        {
            OperationParams parameters = (OperationParams)state;

            try
            {
                mDisplayProgress = true;

                mStatus = Status.ConfiguringCredentials;

                TokenExchangeResponse tokenExchangeResponse =
                    AutoConfig.PlasticCredentials(
                        parameters.AccessToken,
                        parameters.RepositorySpec.Server);

                if (tokenExchangeResponse.Error != null)
                {
                    mOperationFailed = true;

                    LogTokenExchangeErrorInConsole(tokenExchangeResponse.Error);
                    return;
                }

                mStatus = Status.CreatingWorkspace;

                TrackFeatureUseEvent.For(
                    parameters.RepositorySpec, TrackFeatureUseEvent.
                        Features.UnityPackage.CreateWorkspaceFromHub);

                mCreatedWkInfo = CreateWorkspaceForRepSpec(
                    parameters.RepositorySpec,
                    parameters.WorkspaceFullPath,
                    mLog);

                mStatus = Status.PerformingInitialCheckin;

                PerformInitialCheckinIfRepositoryIsEmpty(
                    mCreatedWkInfo, parameters.RepositorySpec,
                    PlasticGui.Plastic.API, mLog);
            }
            catch (Exception ex)
            {
                LogException(ex);
                LogExceptionErrorInConsole(ex);

                mOperationFailed = true;
            }
            finally
            {
                mStatus = Status.Finished;
            }
        }

        static void PerformInitialCheckinIfRepositoryIsEmpty(
            WorkspaceInfo wkInfo,
            RepositorySpec repositorySpec,
            IPlasticAPI plasticApi,
            ILog log)
        {
            try
            {
                bool isEmptyRepository = IsEmptyRepositoryCondition.
                    Evaluate(wkInfo, repositorySpec, plasticApi);

                if (!isEmptyRepository)
                    return;

                PerformInitialCheckin.ForWorkspace(wkInfo, false, plasticApi);

                log.DebugFormat("Created initial checkin on repository '{0}'",
                    repositorySpec.ToString());
            }
            catch (Exception ex)
            {
                // create the initial checkin if it's possible, otherwise
                // just log the exception (no error shown for the user)
                LogException(ex);
            }
        }

        static void DisplayProgress(
            Status status,
            string repository)
        {
            string progressMessage = GetProgressString(status);

            float progressPercent = (int)status / (float)Status.Finished;

            EditorUtility.DisplayProgressBar(
                string.Format("{0} {1}",
                    PlasticLocalization.Name.CreatingWorkspaceProgress.GetString(),
                    repository),
                progressMessage, progressPercent);
        }

        static void LogTokenExchangeErrorInConsole(ErrorResponse.ErrorFields error)
        {
            UnityEngine.Debug.LogErrorFormat(
                PlasticLocalization.Name.ErrorCreatingWorkspaceForProject.GetString(),
                string.Format("Unable to get TokenExchangeResponse: {0} [code {1}]",
                    error.Message, error.ErrorCode));
        }

        static void LogExceptionErrorInConsole(Exception ex)
        {
            UnityEngine.Debug.LogErrorFormat(
                PlasticLocalization.Name.ErrorCreatingWorkspaceForProject.GetString(),
                ex.Message);
        }

        static void LogException(Exception ex)
        {
            mLog.WarnFormat("Message: {0}", ex.Message);

            mLog.DebugFormat("StackTrace:{0}{1}", Environment.NewLine, ex.StackTrace);
        }

        static string GetProgressString(Status status)
        {
            switch (status)
            {
                case Status.Starting:
                    return PlasticLocalization.GetString(
                        PlasticLocalization.Name.CreateWorkspaceProgressStarting);
                case Status.ConfiguringCredentials:
                    return PlasticLocalization.GetString(
                        PlasticLocalization.Name.CreateWorkspaceProgressConfiguringCredentials);
                case Status.CreatingWorkspace:
                    return PlasticLocalization.GetString(
                        PlasticLocalization.Name.CreateWorkspaceProgressCreatingWorkspace);
                case Status.PerformingInitialCheckin:
                    return PlasticLocalization.GetString(
                        PlasticLocalization.Name.CreateWorkspaceProgressPerformingInitialCheckin);
                case Status.Finished:
                    return PlasticLocalization.GetString(
                        PlasticLocalization.Name.CreateWorkspaceProgressFinished);
            }

            return string.Empty;
        }

        CreateWorkspace()
        {
        }

        enum Status : int
        {
            Starting = 1,
            ConfiguringCredentials = 2,
            CreatingWorkspace = 3,
            PerformingInitialCheckin = 4,
            Finished = 5
        };

        volatile Status mStatus = Status.Starting;
        volatile bool mOperationFailed = false;
        volatile bool mDisplayProgress;
        volatile WorkspaceInfo mCreatedWkInfo;

        const double IDLE_DELAY_SECONDS = 1.0;

        static bool mIsOperationInProgress;

        static readonly ILog mLog = PlasticApp.GetLogger("CreateWorkspace");
    }
}