﻿using System.Collections.Generic;
using EasyApi.Metadata;
using EasyApi.Utils;

namespace EasyApi.Binding
{
	public class ParameterBinder : IParameterBinder
	{
		private static readonly IDictionary<string, ParameterBindingCreationCallback> _defaultBindingMap = new Dictionary<string, ParameterBindingCreationCallback>
		{
			[KnownBindingNames.Uri] = CreateUriBinding,
			[KnownBindingNames.Body] = CreateBodyBinding
		};

		private readonly IDictionary<string, ParameterBindingCreationCallback> _bindingMap;

		public ParameterBinder(IDictionary<string, ParameterBindingCreationCallback> bindingMap)
		{
			Argument.IsNotNull(bindingMap, nameof(bindingMap));

			_bindingMap = new Dictionary<string, ParameterBindingCreationCallback>(bindingMap);
		}

		public ParameterBinder()
		{
			_bindingMap = new Dictionary<string, ParameterBindingCreationCallback>();
		}

		private static IParameterBinding CreateUriBinding(IParameterMetadata parameterMetadata, IActionMetadata actionMetadata)
		{
			return new UriParameterBinding(parameterMetadata, actionMetadata);
		}

		private static IParameterBinding CreateBodyBinding(IParameterMetadata parameterMetadata, IActionMetadata actionMetadata)
		{
			return new BodyParameterBinding(parameterMetadata, actionMetadata);
		}

		private ParameterBindingCreationCallback GetBindingCreationCallback(IParameterMetadata parameterMetadata, IActionMetadata actionMetadata)
		{
			ParameterBindingCreationCallback bindingCreationCallback;
			foreach (var map in actionMetadata.GetPropertyValueChain(MetadataProperties.ParameterBindingMap))
			{
				if (map.TryGetValue(parameterMetadata.BindingName, out bindingCreationCallback) && bindingCreationCallback != null)
				{
					return bindingCreationCallback;
				}
			}

			if (_bindingMap.TryGetValue(parameterMetadata.BindingName, out bindingCreationCallback) && bindingCreationCallback != null)
			{
				return bindingCreationCallback;
			}

			if (_defaultBindingMap.TryGetValue(parameterMetadata.BindingName, out bindingCreationCallback))
			{
				return bindingCreationCallback;
			}

			return null;
		}

		public IParameterBinding GetBinding(IParameterMetadata parameterMetadata, IActionMetadata actionMetadata)
		{
			Argument.IsNotNull(parameterMetadata, nameof(parameterMetadata));
			Argument.IsNotNull(actionMetadata, nameof(actionMetadata));

			var bindingCreationCallback = GetBindingCreationCallback(parameterMetadata, actionMetadata);
			if (bindingCreationCallback == null)
			{
				return null;
			}

			return bindingCreationCallback(parameterMetadata, actionMetadata);
		}
	}
}