using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Amazon.Common.DotNetCli.Tools;
using Amazon.Common.DotNetCli.Tools.Options;
using Amazon.Lambda.Model;
using ThirdParty.Json.LitJson;
namespace Amazon.Lambda.Tools.Commands
{
    /// 
    /// Invoke a function running in Lambda
    /// 
    public class InvokeFunctionCommand : LambdaBaseCommand
    {
        public const string COMMAND_NAME = "invoke-function";
        public const string COMMAND_DESCRIPTION = "Command to invoke a function in Lambda with an optional input";
        public const string COMMAND_ARGUMENTS = " The name of the function to invoke";
        public static readonly IList InvokeCommandOptions = BuildLineOptions(new List
        {
            LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_NAME,
            LambdaDefinedCommandOptions.ARGUMENT_PAYLOAD
        });
        public string FunctionName { get; set; }
        /// 
        /// If the value for Payload points to an existing file then the contents of the file is sent, otherwise
        /// the value of Payload is sent as the input to the function.
        /// 
        public string Payload { get; set; }
        public InvokeFunctionCommand(IToolLogger logger, string workingDirectory, string[] args)
            : base(logger, workingDirectory, InvokeCommandOptions, args)
        {
        }
        /// 
        /// Parse the CommandOptions into the Properties on the command.
        /// 
        /// 
        protected override void ParseCommandArguments(CommandOptions values)
        {
            base.ParseCommandArguments(values);
            if(values.Arguments.Count > 0)
            {
                this.FunctionName = values.Arguments[0];
            }
            Tuple tuple;
            if ((tuple = values.FindCommandOption(LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_NAME.Switch)) != null)
                this.FunctionName = tuple.Item2.StringValue;
            if ((tuple = values.FindCommandOption(LambdaDefinedCommandOptions.ARGUMENT_PAYLOAD.Switch)) != null)
                this.Payload = tuple.Item2.StringValue;
        }
        protected override async Task PerformActionAsync()
        {
            var invokeRequest = new InvokeRequest
            {
                FunctionName = this.GetStringValueOrDefault(this.FunctionName, LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_NAME, true),
                LogType = LogType.Tail
            };
            if (!string.IsNullOrWhiteSpace(this.Payload))
            {
                if (File.Exists(this.Payload))
                {
                    Logger.WriteLine($"Reading {Path.GetFullPath(this.Payload)} as input to Lambda function");
                    invokeRequest.Payload = File.ReadAllText(this.Payload);
                }
                else
                {
                    invokeRequest.Payload = this.Payload.Trim();
                }
                if(!invokeRequest.Payload.StartsWith("{"))
                {
                    invokeRequest.Payload = "\"" + invokeRequest.Payload + "\"";
                }
            }
            InvokeResponse response;
            try
            {
                await LambdaUtilities.WaitTillFunctionAvailableAsync(this.Logger, this.LambdaClient, invokeRequest.FunctionName);
                response = await this.LambdaClient.InvokeAsync(invokeRequest);
            }
            catch(Exception e)
            {
                throw new LambdaToolsException("Error invoking Lambda function: " + e.Message, LambdaToolsException.LambdaErrorCode.LambdaInvokeFunction, e);
            }
            this.Logger.WriteLine("Payload:");
            PrintPayload(response);
            this.Logger.WriteLine("");
            this.Logger.WriteLine("Log Tail:");
            var log = System.Text.UTF8Encoding.UTF8.GetString(Convert.FromBase64String(response.LogResult));
            this.Logger.WriteLine(log);
            return true;
        }
        private void PrintPayload(InvokeResponse response)
        {
            try
            {
                var payload = new StreamReader(response.Payload).ReadToEnd();
                this.Logger.WriteLine(payload);
            }
            catch (Exception)
            {
                this.Logger.WriteLine("");
            }
        }
        
        protected override void SaveConfigFile(JsonData data)
        {
            data.SetIfNotNull(LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_NAME.ConfigFileKey, this.GetStringValueOrDefault(this.FunctionName, LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_NAME, false));    
            data.SetIfNotNull(LambdaDefinedCommandOptions.ARGUMENT_PAYLOAD.ConfigFileKey, this.GetStringValueOrDefault(this.Payload, LambdaDefinedCommandOptions.ARGUMENT_PAYLOAD, false));              
        }
    }
}