#if WorkItems
using File_Folder_Helper.ADO2024.PI3.WorkItems;
#endif
using Microsoft.Extensions.Logging;
#if WorkItems
using Microsoft.TeamFoundation.WorkItemTracking.WebApi;
using Microsoft.VisualStudio.Services.Common;
using Microsoft.VisualStudio.Services.WebApi;
using System.Collections.ObjectModel;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Web;
#endif

namespace File_Folder_Helper.ADO2024.PI3;

internal static partial class Helper20240830
{

#if WorkItems

    private record WorkItem(string AreaPath,
                           string? AssignedTo,
                           int? BusinessValue,
                           DateTime ChangedDate,
                           DateTime? ClosedDate,
                           int CommentCount,
                           DateTime CreatedDate,
                           string Description,
                           float? Effort,
                           int Id,
                           string IterationPath,
                           int? Parent,
                           int? Priority,
                           object[] Relations,
                           string? Requester,
                           DateTime? ResolvedDate,
                           int Revision,
                           int? RiskReductionMinusOpportunityEnablement,
                           DateTime? StartDate,
                           string State,
                           string Tags,
                           DateTime? TargetDate,
                           float? TimeCriticality,
                           string Title,
                           string WorkItemType,
                           float? WeightedShortestJobFirst);

    private static void CompareWorkItems(ILogger<Worker> logger, string sourceDirectory, string api, string site, string query, string project, string basePage, string baseAddress, byte[] bytes, MediaTypeWithQualityHeaderValue mediaTypeWithQualityHeaderValue, WorkItemTrackingHttpClient workItemTrackingHttpClient, HttpClient httpClient)
    {
        string base64 = Convert.ToBase64String(bytes);
        httpClient.DefaultRequestHeaders.Authorization = new("Basic", base64);
        httpClient.DefaultRequestHeaders.Accept.Add(mediaTypeWithQualityHeaderValue);
        logger.LogInformation("{baseAddress}{basePage}/{project}{api}{query}", baseAddress, basePage, HttpUtility.HtmlEncode(project), api, query);
        CompareWorkItems(httpClient, sourceDirectory, basePage, api, query, workItemTrackingHttpClient, project, site);
    }

    private static void GetSingle(HttpClient httpClient, string basePage, string api, string targetFileLocation, int id)
    {
        Task<HttpResponseMessage> httpResponseMessageTask = httpClient.GetAsync(string.Concat(basePage, api, $"/workitems/{id}?%24expand=1"));
        httpResponseMessageTask.Wait();
        if (!httpResponseMessageTask.Result.IsSuccessStatusCode)
            throw new Exception(httpResponseMessageTask.Result.StatusCode.ToString());
        Task<Stream> streamTask = httpResponseMessageTask.Result.Content.ReadAsStreamAsync();
        streamTask.Wait();
        if (!streamTask.Result.CanRead)
            throw new NullReferenceException(nameof(streamTask));
        JsonElement? result = JsonSerializer.Deserialize<JsonElement>(streamTask.Result);
        string file = Path.Combine(targetFileLocation, $"{-9}-{id}.json");
        File.WriteAllText(file, result.ToString());
    }

    internal static void CompareWorkItems(ILogger<Worker> logger, List<string> args)
    {
        string api = args[6];
        string pat = args[8];
        string site = args[2];
        string query = args[7];
        string project = args[5];
        string basePage = args[4];
        string baseAddress = args[3];
        string sourceDirectory = args[0];
        VssBasicCredential credential = new("", pat);
        byte[] bytes = Encoding.ASCII.GetBytes($":{pat}");
        VssConnection connection = new(new(string.Concat(baseAddress, basePage)), credential);
        MediaTypeWithQualityHeaderValue mediaTypeWithQualityHeaderValue = new("application/json");
        WorkItemTrackingHttpClient workItemTrackingHttpClient = connection.GetClient<WorkItemTrackingHttpClient>();
        HttpClient httpClient = new(new HttpClientHandler() { UseDefaultCredentials = true }) { BaseAddress = new(baseAddress) };
        CompareWorkItems(logger, sourceDirectory, api, site, query, project, basePage, baseAddress, bytes, mediaTypeWithQualityHeaderValue, workItemTrackingHttpClient, httpClient);
    }

    private static ReadOnlyCollection<WorkItem> GetWorkItems(ReadOnlyCollection<ValueWithReq> valueWithReqCollection)
    {
        List<WorkItem> results = [];
        Fields fields;
        WorkItem workItem;
        foreach (ValueWithReq valueWithReq in valueWithReqCollection)
        {
            fields = valueWithReq.Value.Fields;
            workItem = new(fields.SystemAreaPath,
                           fields.SystemAssignedTo?.DisplayName,
                           fields.MicrosoftVSTSCommonBusinessValue == 0 ? null : fields.MicrosoftVSTSCommonBusinessValue,
                           fields.SystemChangedDate,
                           fields.MicrosoftVSTSCommonClosedDate == DateTime.MinValue ? null : fields.MicrosoftVSTSCommonClosedDate,
                           fields.SystemCommentCount,
                           fields.SystemCreatedDate,
                           fields.SystemDescription,
                           fields.MicrosoftVSTSSchedulingEffort == 0 ? null : fields.MicrosoftVSTSSchedulingEffort,
                           valueWithReq.Value.Id,
                           fields.SystemIterationPath,
                           fields.SystemParent == 0 ? null : fields.SystemParent,
                           fields.MicrosoftVSTSCommonPriority == 0 ? null : fields.MicrosoftVSTSCommonPriority,
                           valueWithReq.Value.Relations,
                           fields.CustomRequester?.DisplayName,
                           fields.MicrosoftVSTSCommonResolvedDate == DateTime.MinValue ? null : fields.MicrosoftVSTSCommonResolvedDate,
                           valueWithReq.Value.Rev,
                           fields.CustomRRminusOE == 0 ? null : fields.CustomRRminusOE,
                           fields.MicrosoftVSTSSchedulingStartDate == DateTime.MinValue ? null : fields.MicrosoftVSTSSchedulingStartDate,
                           fields.SystemState,
                           fields.SystemTags,
                           fields.MicrosoftVSTSSchedulingTargetDate == DateTime.MinValue ? null : fields.MicrosoftVSTSSchedulingTargetDate,
                           fields.MicrosoftVSTSCommonTimeCriticality == 0 ? null : fields.MicrosoftVSTSCommonTimeCriticality,
                           fields.SystemTitle,
                           fields.SystemWorkItemType,
                           fields.CustomWSJF == 0 ? null : fields.CustomWSJF);
            results.Add(workItem);
        }
        return new(results);
    }

    private static string GetIds(HttpClient httpClient, string basePage, string api, string query)
    {
        List<int> results = [];
        StringBuilder result = new();
        Task<HttpResponseMessage> httpResponseMessageTask = httpClient.GetAsync(string.Concat(basePage, api, query));
        httpResponseMessageTask.Wait();
        if (!httpResponseMessageTask.Result.IsSuccessStatusCode)
            throw new Exception(httpResponseMessageTask.Result.StatusCode.ToString());
        Task<Stream> streamTask = httpResponseMessageTask.Result.Content.ReadAsStreamAsync();
        streamTask.Wait();
        if (!streamTask.Result.CanRead)
            throw new NullReferenceException(nameof(streamTask));
        WIQL.Root? root = JsonSerializer.Deserialize(streamTask.Result, WIQL.WIQLRootSourceGenerationContext.Default.Root);
        streamTask.Result.Dispose();
        if (root is null || root.WorkItems is null)
            throw new NullReferenceException(nameof(root));
        foreach (WIQL.WorkItem workItem in root.WorkItems)
        {
            results.Add(workItem.Id);
            if (results.Count > 199)
                break;
        }
        foreach (int id in results)
            _ = result.Append(id).Append(',');
        if (result.Length > 0)
            _ = result.Remove(result.Length - 1, 1);
        return result.ToString();
    }

    private static ReadOnlyCollection<ValueWithReq> GetWorkItems(HttpClient httpClient, string basePage, string api, string targetFileLocation, string ids)
    {
        List<ValueWithReq> results = [];
        string json;
        string file;
        Value? value;
        JsonElement[] jsonElements;
        Task<HttpResponseMessage> httpResponseMessageTask = httpClient.GetAsync(string.Concat(basePage, api, $"/workitems?ids={ids}&$expand=Relations"));
        httpResponseMessageTask.Wait();
        if (!httpResponseMessageTask.Result.IsSuccessStatusCode)
            throw new Exception(httpResponseMessageTask.Result.StatusCode.ToString());
        Task<Stream> streamTask = httpResponseMessageTask.Result.Content.ReadAsStreamAsync();
        streamTask.Wait();
        if (!streamTask.Result.CanRead)
            throw new NullReferenceException(nameof(streamTask));
        JsonElement? result = JsonSerializer.Deserialize<JsonElement>(streamTask.Result);
        if (result is null || result.Value.ValueKind != JsonValueKind.Object)
            throw new NullReferenceException(nameof(result));
        JsonProperty[] jsonProperties = result.Value.EnumerateObject().ToArray();
        foreach (JsonProperty jsonProperty in jsonProperties)
        {
            if (jsonProperty.Value.ValueKind != JsonValueKind.Array)
                continue;
            jsonElements = jsonProperty.Value.EnumerateArray().ToArray();
            foreach (JsonElement jsonElement in jsonElements)
            {
                json = jsonElement.GetRawText();
                value = JsonSerializer.Deserialize(json, ValueSourceGenerationContext.Default.Value);
                if (value is null)
                    continue;
                if (value.Id == 120593)
                    GetSingle(httpClient, basePage, api, targetFileLocation, value.Id);
                file = Path.Combine(targetFileLocation, $"{-1}-{value.Id}.json");
                File.WriteAllText(file, json);
                results.Add(new(value, -1, json));
            }
        }
        return new(results);
    }

    private static void CompareWorkItems(WorkItemTrackingHttpClient workItemTrackingHttpClient,
                                         string sourceDirectory,
                                         string project,
                                         string site,
                                         ReadOnlyCollection<ValueWithReq> valueWithReqCollection)
    {
        ArgumentNullException.ThrowIfNull(workItemTrackingHttpClient);
        if (string.IsNullOrEmpty(project))
            throw new ArgumentException($"'{nameof(project)}' cannot be null or empty.", nameof(project));
        if (string.IsNullOrEmpty(sourceDirectory))
            throw new ArgumentException($"'{nameof(sourceDirectory)}' cannot be null or empty.", nameof(site));
        if (string.IsNullOrEmpty(site))
            throw new ArgumentException($"'{nameof(site)}' cannot be null or empty.", nameof(site));
        ReadOnlyCollection<WorkItem> workItems = GetWorkItems(valueWithReqCollection);
        string file = Path.Combine(sourceDirectory, $"_.json");
        string json = JsonSerializer.Serialize(workItems);
        File.WriteAllText(file, json);
        foreach (WorkItem workItem in workItems)
        {
            if (workItem is null)
            { }
        }
        // https://stackoverflow.com/questions/18153998/how-do-i-remove-all-html-tags-from-a-string-without-knowing-which-tags-are-in-it
    }

    private static void CompareWorkItems(HttpClient httpClient,
                                        string targetFileLocation,
                                        string basePage,
                                        string api,
                                        string query,
                                        WorkItemTrackingHttpClient workItemTrackingHttpClient,
                                        string project,
                                        string site)
    {
        string ids = GetIds(httpClient, basePage, api, query);
        ReadOnlyCollection<ValueWithReq> valueWithReqCollection = string.IsNullOrEmpty(ids) ? new([]) : GetWorkItems(httpClient, basePage, api, targetFileLocation, ids);
        CompareWorkItems(workItemTrackingHttpClient, targetFileLocation, project, site, valueWithReqCollection);
    }

#else

    internal static void CompareWorkItems(ILogger<Worker> logger, List<string> args)
    {
        logger.LogError("CompareWorkItems is not available in WorkItems {args[0]}", args[0]);
        logger.LogError("CompareWorkItems is not available in WorkItems {args[1]}", args[1]);
    }

#endif

}