using System;
using System.Collections.Generic;
using System.Linq;

// ReSharper disable PossibleInvalidOperationException

namespace Monithor.api.Monitors {
	public class HttpMonitor : IMonitor {
		private readonly string _server;

		public string Identifier { get; set; }

		public HttpMonitor(string identifier, string server) {
			Identifier = identifier;
			_server    = server;
		}

		public string        LastValue  { get; set; }
		public DateTime      StartedAt  { get; set; }
		public DateTime      ResolvedAt { get; set; }
		public MonitorStatus Status     { get; set; }
		public string        Message    { get; set; }

		public IEnumerable<FluxItem> GetFluxItems(Influx influx, string timespan) {
			var query = $"from(bucket: \"{influx.Bucket}\")"
						+ $"|> range(start: -5m)"
						+ $"|> filter(fn: (r) => r[\"_measurement\"] == \"http_response\")"
						+ $"|> filter(fn: (r) => r[\"server\"] == \"{_server}\")"
						+ $"|> keep(columns:[\"_field\", \"_time\", \"_value\"])"
						+ $"|> pivot(rowKey:[\"_time\"], columnKey: [\"_field\"], valueColumn: \"_value\")"
						+ $"|> sort(columns: [\"_time\"], desc: false)";

			var result = influx.Client.GetQueryApi().QueryAsync(query, influx.Org).Result;

			return (from table in result
					from record in table.Records
					select new FluxItem {
						Time    = record.GetTime().Value,
						Status  = record.GetValueByKey("result_type").ToString(),
						Message = (Math.Ceiling((double) (record.GetValueByKey("response_time") ?? -1d) * 1000) + " ms").Replace("-1000 ms", "n/a")
					}).ToList();
		}

		public bool Update(IEnumerable<FluxItem> data) {
			if (!data.Any()) {
				if (Status == MonitorStatus.Down)
					return false;

				Status = MonitorStatus.Down;
				return true;
			}

			if (Status == MonitorStatus.Unknown) {
				Status = MonitorStatus.Up;
			}

			var anyDown = data.Any(p => p.Status != "success");
			var allDown = data.All(p => p.Status != "success");

			LastValue = data.Last().Status;
			Message   = data.Last().Message;

			switch (Status) {
				case MonitorStatus.Down when anyDown && !allDown:
					Status     = MonitorStatus.Recovering;
					ResolvedAt = DateTime.Now;
					return true;
				case MonitorStatus.Down when allDown: return false;
				case MonitorStatus.Down:
					Status = MonitorStatus.Up;
					return true;
				case MonitorStatus.Validating when anyDown && !allDown: return false;
				case MonitorStatus.Validating when allDown:
					Status = MonitorStatus.Down;
					return true;
				case MonitorStatus.Validating:
					Status = MonitorStatus.Up;
					return false;
				case MonitorStatus.Recovering when anyDown && !allDown: return false;
				case MonitorStatus.Recovering when allDown:
					Status = MonitorStatus.Down;
					return true;
				case MonitorStatus.Recovering:
					Status = MonitorStatus.Up;
					return true;
				case MonitorStatus.Up when anyDown && !allDown:
					Status    = MonitorStatus.Validating;
					StartedAt = DateTime.Now;
					return false;
				case MonitorStatus.Up when allDown:
					Status    = MonitorStatus.Down;
					StartedAt = DateTime.Now;
					return true;
				case MonitorStatus.Up: return false;
				default:
					Status = MonitorStatus.Up;
					return false;
			}
		}
	}
}