using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Web;
using System.Web.Mvc;
using HtmlTags;
using Humanizer;
using InventoryTraker.Web.Utilities;

namespace InventoryTraker.Web.Helpers
{
	public class AngularModelHelper<TModel>
	{
		protected readonly HtmlHelper Helper;
		private readonly string _expressionPrefix;

		public AngularModelHelper(HtmlHelper helper, string expressionPrefix)
		{
			Helper = helper;
			_expressionPrefix = expressionPrefix;
		}

		/// <summary>
		/// Converts an lambda expression into a camel-cased string, prefixed
		/// with the helper's configured prefix expression, ie:
		/// vm.model.parentProperty.childProperty
		/// </summary>
		public IHtmlString ExpressionFor<TProp>(Expression<Func<TModel, TProp>> property)
		{
			var expressionText = ExpressionForInternal(property);
			return new MvcHtmlString(expressionText);
		}

		/// <summary>
		/// Converts a lambda expression into a camel-cased AngularJS binding expression, ie:
		/// {{vm.model.parentProperty.childProperty}} 
		/// </summary>
		public IHtmlString BindingFor<TProp>(Expression<Func<TModel, TProp>> property, string filter = "")
		{
			return MvcHtmlString.Create("{{" 
				+ ExpressionForInternal(property) 
				+ (!string.IsNullOrEmpty(filter) ? " | " + filter : string.Empty) 
				+ "}}");
		}

		/// <summary>
		/// Creates a div with an ng-repeat directive to enumerate the specified property,
		/// and returns a new helper you can use for strongly-typed bindings on the items
		/// in the enumerable property.
		/// </summary>
		public AngularNgRepeatHelper<TSubModel> Repeat<TSubModel>(
			Expression<Func<TModel, IEnumerable<TSubModel>>> property, string variableName)
		{
			var propertyExpression = ExpressionForInternal(property);
			return new AngularNgRepeatHelper<TSubModel>(
				Helper, variableName, propertyExpression);
		}

		private string ExpressionForInternal<TProp>(Expression<Func<TModel, TProp>> property)
		{
			var camelCaseName = property.ToCamelCaseName();

			var expression = !string.IsNullOrEmpty(_expressionPrefix)
				? _expressionPrefix + "." + camelCaseName
				: camelCaseName;

			return expression;
		}

		public HtmlTag FormGroupFor<TProp>(Expression<Func<TModel, TProp>> property)
		{
			var metadata = ModelMetadata.FromLambdaExpression(property, new ViewDataDictionary<TModel>());
			
			var name = ExpressionHelper.GetExpressionText(property);

			var expression = ExpressionForInternal(property);

			var labelText = metadata.DisplayName ?? name.Humanize(LetterCasing.Title);

			//Creates <label class="control-label" for="Name">Name</label>
			var label = new HtmlTag("label")
				.Attr("for", name)
				.Text(labelText);

			var tagName =
				metadata.DataTypeName == "MultilineText" 
				? "textarea" 
				: "input";

			//Creates <input ng-model="expression"
			//		   class="form-control" name="Name" type="text" >
			var input = new HtmlTag(tagName)
				.Attr("ng-model", expression)
				.Attr("name", name);

			var formGroup = new HtmlTag("div");

			if (metadata.ModelType != typeof(bool))
			{
				label.AddClass("control-label");

				var placeholder = metadata.Watermark ??
					  labelText + "...";

				input
					.AddClass("form-control")
					.Attr("type", "text")
					.Attr("placeholder", placeholder);

				ApplyValidationToInput(input, metadata);

				//Creates <div class="form-group has-feedback"
				//				form-group-validation="Name">
				formGroup
					.AddClass("form-group")
					.AddClass("has-feedback")
					.Attr("form-group-validation", name)
					.Append(label)
					.Append(input);
			}
			else if (metadata.ModelType == typeof(bool))
			{
				label.AddClass("form-check-label");

				input
					.AddClass("form-check-input")
					.Attr("type", "checkbox");

				label.Text("")
					.Append(input)
					.AppendHtml("&nbsp;&nbsp;")
					.Append(new HtmlTag("text").NoTag().Text(labelText));

				formGroup
					.AddClass("form-check")
					.Append(label);
			}

			return formGroup;
		}

		private void ApplyValidationToInput(HtmlTag input, ModelMetadata metadata)
		{
			if (metadata.IsRequired)
				input.Attr("required", "");

			if (metadata.DataTypeName == "EmailAddress")
				input.Attr("type", "email");

			if (metadata.DataTypeName == "Password")
				input.Attr("type", "password");

			if (metadata.ModelType == typeof(DateTime))
			{
				//input.Attr("type", "date");
				input.Attr("bs-datepicker");
				input.Attr("data-date-format", "M/d/yyyy");
			}

			if (metadata.DataTypeName == "PhoneNumber")
				input.Attr("pattern", @"[\ 0-9()-]+");
		}
	}
}