﻿using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using EasyApi.Metadata;
using EasyApi.Utils;

namespace EasyApi.Formatting
{
	public abstract class MediaTypeFormatterSelector : IMediaTypeFormatterSelector
	{
		private static MediaTypeFormatterCollection GetMediaTypeFormatters(IActionMetadata actionMetadata)
		{
			return actionMetadata.LookupPropertyValue(MetadataProperties.MediaTypeFormatterCollection);
		}

		public MediaTypeFormatter SelectContentReader(MediaTypeHeaderValue mediaType, IValueMetadata valueMetadata, IActionMetadata actionMetadata)
		{
			Argument.IsNotNull(valueMetadata, nameof(valueMetadata));
			Argument.IsNotNull(mediaType, nameof(mediaType));
			Argument.IsNotNull(actionMetadata, nameof(actionMetadata));

			return SelectContentReader(mediaType, GetMediaTypeFormatters(actionMetadata), valueMetadata, actionMetadata);
		}

		protected abstract MediaTypeFormatter SelectContentReader(
			MediaTypeHeaderValue mediaType,
			MediaTypeFormatterCollection mediaTypeFormatters,
			IValueMetadata valueMetadata,
			IActionMetadata actionMetadata
		);

		public IEnumerable<MediaTypeFormatter> SelectAvailableReaders(IValueMetadata valueMetadata, IActionMetadata actionMetadata)
		{
			Argument.IsNotNull(valueMetadata, nameof(valueMetadata));
			Argument.IsNotNull(actionMetadata, nameof(actionMetadata));

			return SelectAvailableReaders(GetMediaTypeFormatters(actionMetadata), valueMetadata, actionMetadata);
		}

		protected abstract IEnumerable<MediaTypeFormatter> SelectAvailableReaders(
			MediaTypeFormatterCollection mediaTypeFormatters,
			IValueMetadata valueMetadata,
			IActionMetadata actionMetadata
		);

		public MediaTypeFormatter SelectContentWriter(IValueMetadata valueMetadata, IActionMetadata actionMetadata)
		{
			Argument.IsNotNull(valueMetadata, nameof(valueMetadata));
			Argument.IsNotNull(actionMetadata, nameof(actionMetadata));

			return SelectContentWriter(GetMediaTypeFormatters(actionMetadata), valueMetadata, actionMetadata);
		}

		protected abstract MediaTypeFormatter SelectContentWriter(MediaTypeFormatterCollection mediaTypeFormatters, IValueMetadata valueMetadata, IActionMetadata actionMetadata);

		public ContentNegotiationResult NegotiateContent(HttpRequestMessage request, IValueMetadata valueMetadata, IActionMetadata actionMetadata)
		{
			Argument.IsNotNull(valueMetadata, nameof(valueMetadata));
			Argument.IsNotNull(request, nameof(request));
			Argument.IsNotNull(actionMetadata, nameof(actionMetadata));

			return NegotiateContent(request, GetMediaTypeFormatters(actionMetadata), valueMetadata, actionMetadata);
		}

		protected abstract ContentNegotiationResult NegotiateContent(
			HttpRequestMessage request,
			MediaTypeFormatterCollection mediaTypeFormatters,
			IValueMetadata valueMetadata,
			IActionMetadata actionMetadata
		);
	}
}