//  Copyright (c) Microsoft Corporation.  All rights reserved.

namespace Microsoft.VisualStudio.TestTools.TestCaseMigrator
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Globalization;
    using Microsoft.TeamFoundation;
    using Microsoft.TeamFoundation.Client;
    using Microsoft.TeamFoundation.TestManagement.Client;
    using Microsoft.TeamFoundation.WorkItemTracking.Client;

    internal class WorkItemGenerator : NotifyPropertyChange, IWorkItemGenerator
    {
        #region Fields

        private string m_serverName;
        private string m_projectName;
        private TfsTeamProjectCollection m_server;
        private ITestManagementTeamProject m_teamProject;
        private WorkItemCategory m_workItemCategory;
        private string m_workItemTypeName;

        #endregion

        #region Constants

        // TestCase Category Reference Name
        private const string TestCaseCategory = "Microsoft.TestCaseCategory";

        // Refernce Name prefix for TCM Fields
        private const string TCMField = "Microsoft.VSTS.TCM";

        // Reference Name for WIT Field: Test Steps
        private const string StepsReferenceName = "Microsoft.VSTS.TCM.Steps";

        #endregion

        #region Constructor

        public WorkItemGenerator(string serverName, string projectName)
        {
            if (string.IsNullOrEmpty(serverName))
            {
                throw new ArgumentNullException("serverName", "servername is null");
            }
            if (string.IsNullOrEmpty(projectName))
            {
                throw new ArgumentNullException("projectName", "projectName is null");
            }

            // Initialize the server Connnection
            m_server = new TfsTeamProjectCollection(new Uri(serverName));

            // Get the Test Management Service
            ITestManagementService service = (ITestManagementService)m_server.GetService(typeof(ITestManagementService));

            // Initialize the TestManagement Team Project
            m_teamProject = (ITestManagementTeamProject)service.GetTeamProject(projectName);

            // Set the Properties
            Server = serverName;
            Project = projectName;

        }

        #endregion

        #region Properties

        /// <summary>
        /// TFS Server Collection URL
        /// </summary>
        public string Server
        {
            get
            {
                return m_serverName;
            }
            private set
            {
                m_serverName = value;
                NotifyPropertyChanged("Server");
            }
        }

        /// <summary>
        /// TFS Project Name
        /// </summary>
        public string Project
        {
            get
            {
                return m_projectName;
            }
            private set
            {
                m_projectName = value;
                NotifyPropertyChanged("Project");
            }
        }

        /// <summary>
        /// Work Item category of the workitem to migrate
        /// </summary>
        public WorkItemCategory WorkItemCategory
        {
            get
            {
                return m_workItemCategory;
            }
            set
            {
                m_workItemCategory = value;
                InitializeWorkItemTypeNames();
            }
        }

        /// <summary>
        /// List of WorkItem type present in selected workitem category
        /// </summary>
        public IList<string> WorkItemTypeNames
        {
            get;
            private set;
        }

        /// <summary>
        /// Default work item type in the selected workitem category
        /// </summary>
        public string DefaultWorkItemTypeName
        {
            get;
            private set;
        }

        /// <summary>
        /// Work Item Type of the TFS workitems to generates
        /// </summary>
        public string WorkItemTypeName
        {
            get
            {
                return m_workItemTypeName;
            }
            set
            {
                m_workItemTypeName = value;
                InitializeWorkItemFields();
            }
        }

        /// <summary>
        /// Whether to ass 'test step title' and 'test step expected result' field.
        /// </summary>
        public bool AddTestStepsField
        {
            get;
            set;
        }

        /// <summary>
        /// TFS Name to FIeld Mapping
        /// </summary>
        public IDictionary<string, IWorkItemField> TfsNameToFieldMapping
        {
            get;
            private set;
        }

        /// <summary>
        /// Source Name to FIeld Mapping for selected source Fields 
        /// </summary>
        public IDictionary<string, IWorkItemField> SourceNameToFieldMapping
        {
            get;
            set;
        }

        #endregion
        
        #region Public Methods

        /// <summary>
        /// Creates Workitem from Source Workitem
        /// </summary>
        /// <param name="sourceWorkItem"></param>
        /// <returns></returns>
        public IWorkItem CreateWorkItem(SourceWorkItem sourceWorkItem)
        {
            if (sourceWorkItem == null)
            {
                throw new ArgumentNullException("sourceWorkitem", "sourceWorkitem");
            }

            // Initializing the variables
            IWorkItem workItem = null;

            // Initializing the Workitem
            switch (m_workItemCategory)
            {
                case WorkItemCategory.TestCase:
                    TestCase testCase = new TestCase(m_teamProject, WorkItemTypeName);
                    workItem = testCase;
                    break;
                default:
                    throw new InvalidEnumArgumentException();
            }

            workItem.Create();

            // iterating through each field-value pair
            foreach (KeyValuePair<string, object> kvp in sourceWorkItem.FieldValuePairs)
            {
                // Refernce Name of the field which is to be updated
                string fieldName = kvp.Key;

                // value of the field which has to assigned
                object fieldValue = kvp.Value;

                // Gets the correct field reference name
                if (SourceNameToFieldMapping.ContainsKey(kvp.Key))
                {
                    fieldName = SourceNameToFieldMapping[kvp.Key].TfsName;

                    // If it is autogenerated value then just ignore the assignment of value and continue to next iteration
                    if (SourceNameToFieldMapping[kvp.Key].IsAutoGenerated)
                    {
                        continue;
                    }
                }

                string value = kvp.Value as string;

                // value to assign is of type string and it is not null
                if (value != null)
                {
                    // If this value is dataMapped
                    if (SourceNameToFieldMapping.ContainsKey(kvp.Key) && SourceNameToFieldMapping[kvp.Key].ValueMapping.ContainsKey(value))
                    {
                        if (SourceNameToFieldMapping[kvp.Key].ValueMapping[value] == Resources.IgnoreLabel)
                        {
                            continue;
                        }
                        else
                        {
                            // else sets the correct data value
                            fieldValue = SourceNameToFieldMapping[kvp.Key].ValueMapping[value];
                        }
                    }
                }

                // Update the workitem with field-value
                workItem.UpdateField(fieldName, fieldValue);

            }

            // return the workitem
            return workItem;

        }

        /// <summary>
        /// Save TFS Workitem and return result workitem(passed,failed or warning) depending upon the result of migration.
        /// </summary>
        /// <param name="workItem"></param>
        /// <param name="sourceWorkItem"></param>
        /// <returns></returns>
        public SourceWorkItem SaveWorkItem(IWorkItem workItem, SourceWorkItem sourceWorkItem)
        {
            try
            {
                workItem.Save();
            }
            catch (TestCaseMigratorException te)
            {
                return new FailedSourceWorkItem(sourceWorkItem, te.Args.Title);
            }

            SourceWorkItem updatedSourceWorkItem = new SourceWorkItem();
            updatedSourceWorkItem.SourcePath = sourceWorkItem.SourcePath;
            string warning = null;
            int id = -1;

            switch (m_workItemCategory)
            {
                case WorkItemCategory.TestCase:
                    id = ((ITestCase)(workItem.WorkItem)).Id;
                    break;
                default:
                    id = -1;
                    break;
            }


            foreach (var kvp in sourceWorkItem.FieldValuePairs)
            {
                string previousValue = kvp.Value as string;
                if (!string.IsNullOrEmpty(previousValue))
                {
                    if (SourceNameToFieldMapping[kvp.Key].ValueMapping.ContainsKey(previousValue) &&
                        SourceNameToFieldMapping[kvp.Key].ValueMapping[previousValue] != Resources.IgnoreLabel)
                    {
                        previousValue = SourceNameToFieldMapping[kvp.Key].ValueMapping[previousValue];
                    }
                    string tfsFieldName = SourceNameToFieldMapping[kvp.Key].TfsName;
                    string newValue = workItem.GetFieldValue(tfsFieldName).ToString();
                    if (previousValue != newValue && !SourceNameToFieldMapping[kvp.Key].IsHtmlField)
                    {
                        warning += String.Format(CultureInfo.CurrentCulture,
                                                 "Value of Field:{0} is modified from \n'{1}'\n to \n'{2}'",
                                                 kvp.Key,
                                                 previousValue,
                                                 newValue);
                    }
                    updatedSourceWorkItem.FieldValuePairs.Add(kvp.Key, newValue);
                }
                else
                {
                    string fieldName = kvp.Key;
                    if (SourceNameToFieldMapping.ContainsKey(kvp.Key))
                    {
                        fieldName = SourceNameToFieldMapping[kvp.Key].TfsName;
                    }
                    updatedSourceWorkItem.FieldValuePairs.Add(kvp.Key, workItem.GetFieldValue(fieldName));
                }
            }
            if (string.IsNullOrEmpty(warning))
            {
                return new PassedSourceWorkItem(updatedSourceWorkItem, id);
            }
            else
            {
                return new WarningSourceWorkItem(updatedSourceWorkItem, id, warning);
            }
        }

        #endregion

        #region private methods

        private void InitializeWorkItemTypeNames()
        {
            string workItemCategaory = null;
            switch (m_workItemCategory)
            {
                case WorkItemCategory.TestCase:
                    workItemCategaory = TestCaseCategory;
                    break;
                default:
                    throw new InvalidEnumArgumentException("invalid Category");
            }

            WorkItemTypeNames = new List<string>();
            foreach (WorkItemType witype in m_teamProject.WitProject.Categories[workItemCategaory].WorkItemTypes)
            {
                WorkItemTypeNames.Add(witype.Name);
            }

            DefaultWorkItemTypeName = m_teamProject.WitProject.Categories[workItemCategaory].DefaultWorkItemType.Name;
        }

        private void InitializeWorkItemFields()
        {
            TfsNameToFieldMapping = new Dictionary<string, IWorkItemField>();

            if (string.IsNullOrEmpty(WorkItemTypeName))
            {
                return;
            }
            WorkItemType workItemType = m_teamProject.WitProject.WorkItemTypes[WorkItemTypeName];

            WorkItem wi = workItemType.NewWorkItem();

            // iterating through each field present in the work item type
            foreach (Field field in wi.Fields)
            {
                // Steps Field needs special handling
                if (field.FieldDefinition.ReferenceName == StepsReferenceName)
                {
                    if (AddTestStepsField)
                    {
                        WorkItemField testStepTitleField = new TestStepTitleField();
                        WorkItemField testStepExpectedResultField = new TestStepExpectedResultField();
                        TfsNameToFieldMapping.Add(testStepTitleField.TfsName, testStepTitleField);
                        TfsNameToFieldMapping.Add(testStepExpectedResultField.TfsName, testStepExpectedResultField);
                    }
                    else
                    {
                        IWorkItemField wiField = new WorkItemField(field, m_teamProject.WitProject);
                        TfsNameToFieldMapping.Add(wiField.TfsName, wiField);
                    }

                }
                // else if it is not a TCM Field
                else if (!field.FieldDefinition.ReferenceName.Contains(TCMField))
                {
                    IWorkItemField wiField = new WorkItemField(field, m_teamProject.WitProject);
                    TfsNameToFieldMapping.Add(wiField.TfsName, wiField);
                }
            }
        }

        #endregion

        #region IDisposable Implementation

        public void Dispose()
        {
            try
            {
                if (m_server != null)
                {
                    m_server.Dispose();
                }
            }
            catch (TeamFoundationServerException)
            { }
        }

        #endregion
    }
}
