using System;
using System.Collections.Generic;
using System.Net;
using Kreta.Client.ClientBase;
using Kreta.Client.FileService.Configuration;
using Kreta.Client.FileService.Extension;
using Kreta.Client.FileService.Request;
using Kreta.Client.FileService.Response;
using Kreta.Client.FileService.Response.FileFinalize;
using Kreta.Framework;
using Kreta.Framework.Exceptions;
using Newtonsoft.Json;

namespace Kreta.Client.FileService
{
    internal class FileServiceClientV3 : RestSharpClientBase, IFileServiceClientV3
    {
        private readonly IFileServiceClientConfiguration fileServiceClientConfiguration;

        public FileServiceClientV3(IFileServiceClientConfiguration fileServiceClientConfiguration)
        {
            this.fileServiceClientConfiguration = fileServiceClientConfiguration ?? throw new ArgumentNullException(nameof(fileServiceClientConfiguration));
        }

        public GetTokenResponse GetPrivateToken()
        {
            return GetToken(fileServiceClientConfiguration.GetPrivateTokenRequestParameters());
        }

        private GetTokenResponse GetToken(Dictionary<string, string> tokenRequestParameters)
        {
            return ExceptionLogger(() =>
            {
                BaseUrl = fileServiceClientConfiguration.IDPUrl;

                var relativeUri = "/connect/token";

                var response = Post(relativeUri, HeaderExtension.GetFormUrlEncodedHeader(), tokenRequestParameters);

                if (response.StatusCode == HttpStatusCode.OK)
                {
                    var successResponse = JsonConvert.DeserializeObject<GetTokenSuccessResponse>(response.Result);

                    return new GetTokenResponse(successResponse.AccessToken, successResponse.ExpiresIn);
                }

                var failureResponse = JsonConvert.DeserializeObject<GetTokenFailureResponse>(response.Result);

                return new GetTokenResponse(failureResponse.Error);
            });
        }

        public FileUploadResponse Upload(string privateToken, IFileUploadRequest fileUploadRequest)
        {
            return ExceptionLogger(() =>
            {
                BaseUrl = fileServiceClientConfiguration.FileUploadUrl;

                var header = privateToken.GetAuthorizationHeaderWithJson();
                header.Add("X-version", ApiVersions.V3);

                var filePath = new Dictionary<string, string> { { "Utvonal", fileUploadRequest.Path } };
                var relativeUrl = $"/{UrlConstants.Fajlok}/{UrlConstants.Feltoltes}";

                var response = Post(relativeUrl, header, parameters: filePath, fileList: new List<Jira.Model.Request.File>
                {
                    new Jira.Model.Request.File
                    {
                        Name = "fajl",
                        FileName = fileUploadRequest.FileName,
                        Content = fileUploadRequest.Content,
                        ContentType = fileUploadRequest.ContentType,
                    }
                });

                if (response.StatusCode == HttpStatusCode.Unauthorized)
                {
                    return new FileUploadResponse(true);
                }

                if (response.StatusCode == HttpStatusCode.OK)
                {
                    var successResponse = JsonConvert.DeserializeObject<FileUploadSuccessResponse>(response.Result);

                    return new FileUploadResponse(successResponse.FajlAzonosito, successResponse.Utvonal, successResponse.FajlMeretByteLength);
                }

                var failureResponse = JsonConvert.DeserializeObject<FileUploadFailureResponse>(response.Result);

                return new FileUploadResponse(failureResponse.Uzenet);
            });
        }


        public FileFinalizeResponseV3 Veglegesites(string privateToken, FileFinalizeRequestV3 fileFinalizeRequestV3)
        {
            return ExceptionLogger(() =>
            {
                BaseUrl = fileServiceClientConfiguration.FileUploadUrl;

                var header = privateToken.GetAuthorizationHeaderWithJson();
                header.Add("X-version", ApiVersions.V3);

                var response = Post(
                    relativeUri: $"/{UrlConstants.Fajlok}/{UrlConstants.Veglegesites}",
                    header,
                    parameters: new Dictionary<string, string> { { nameof(fileFinalizeRequestV3.FajlId), fileFinalizeRequestV3.FajlId.ToString() }, { nameof(fileFinalizeRequestV3.Utvonal), fileFinalizeRequestV3.Utvonal } });

                if (response.StatusCode == HttpStatusCode.Unauthorized)
                {
                    return new FileFinalizeResponseV3(true);
                }

                var result = JsonConvert.DeserializeObject<FinalizedFileDtoV3>(response.Result);

                return new FileFinalizeResponseV3(result.FajlId, result.IsSikeres, result.HibaSzoveg, false, response.StatusCode, response.ErrorMessage, response.Exception);
            });

        }


        public GetFileResponse GetFile(string intezmenyAzonosito, string privateToken, GetUrlRequest getUrlRequest)
        {
            return ExceptionLogger(() =>
            {
                BaseUrl = fileServiceClientConfiguration.FileUploadUrl;

                var relativeUri = $"/{UrlConstants.Fajlok}/{UrlConstants.Letoltes}";
                var header = privateToken.GetAuthorizationHeaderWithJson();
                header.Add("X-version", ApiVersions.V3);

                var response = Post(relativeUri, header, body: getUrlRequest.GetUrlRequestParametersV3());

                if (response.StatusCode == HttpStatusCode.Unauthorized)
                {
                    return new GetFileResponse(true);
                }

                if (response.StatusCode == HttpStatusCode.OK)
                {
                    return JsonConvert.DeserializeObject<GetFileResponse>(response.Result);
                }

                return new GetFileResponse(response.ErrorMessage);
            });
        }

        public FileDeleteResponseV3 Delete(string intezmenyAzonosito, string privateToken, FileDeleteRequest fileDeleteRequest)
        {
            return ExceptionLogger(() =>
            {
                BaseUrl = fileServiceClientConfiguration.FileUploadUrl;

                var relativeUri = $"/{UrlConstants.Fajlok}/{UrlConstants.Torles}";
                var header = privateToken.GetAuthorizationHeaderWithJson();
                header.Add("X-version", ApiVersions.V3);

                var response = Post(relativeUri, header, body: new[] { new { FajlId = fileDeleteRequest.FajlAzonosito, fileDeleteRequest.Utvonal } });

                if (response.StatusCode == HttpStatusCode.Unauthorized)
                {
                    return new FileDeleteResponseV3(true);
                }

                if (response.StatusCode == HttpStatusCode.OK)
                {
                    return JsonConvert.DeserializeObject<List<FileDeleteResponseV3>>(response.Result)[0];
                }

                return new FileDeleteResponseV3(response.ErrorMessage);
            });
        }

        private T ExceptionLogger<T>(Func<T> action) where T : IResponse, new()
        {
            try
            {
                return action();
            }
            catch (Exception ex)
            {
                SDAServer.Instance.Logger.ExceptionThrown(ex);

                return (T)Activator.CreateInstance(typeof(T), ExceptionUtil.ExceptionToString(ex));
            }
        }
    }

    public static class UrlConstants
    {
        public const string Fajlok = "fajlok";
        public const string FajlUrl = "fajl-url";
        public const string IdeiglenesFajlok = "ideiglenesfajlok";
        public const string Veglegesites = "veglegesites";
        public const string Letoltes = "letoltes";
        public const string Feltoltes = "feltoltes";
        public const string Torles = "torles";
        public const string Rollback = "rollback";
        public const string Clone = "clone";
        public const string Zip = "zip";
    }
    public class ApiVersions
    {
        public const int DefaultMajor = 1;
        public const int DefaultMinor = 0;

        public const string V1 = "1.0";
        public const string V2 = "2.0";
        public const string V3 = "3.0";
    }
}