import I18n from "I18n";
import { helperContext } from "discourse-common/lib/helpers";

export function shortDate(date) {
  return moment(date).format(I18n.t("dates.medium.date_year"));
}

function shortDateNoYear(date) {
  return moment(date).format(I18n.t("dates.tiny.date_month"));
}

// Suppress year if it's this year
export function smartShortDate(date, withYear = tinyDateYear) {
  return date.getFullYear() === new Date().getFullYear()
    ? shortDateNoYear(date)
    : withYear(date);
}

export function tinyDateYear(date) {
  return moment(date).format(I18n.t("dates.tiny.date_year"));
}

// http://stackoverflow.com/questions/196972/convert-string-to-title-case-with-javascript
// TODO: locale support ?
export function toTitleCase(str) {
  return str.replace(/\w\S*/g, function (txt) {
    return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
  });
}

export function longDate(dt) {
  if (!dt) {
    return;
  }
  return moment(dt).format(I18n.t("dates.long_with_year"));
}

// suppress year, if current year
export function longDateNoYear(dt) {
  if (!dt) {
    return;
  }

  if (new Date().getFullYear() !== dt.getFullYear()) {
    return moment(dt).format(I18n.t("dates.long_date_with_year"));
  } else {
    return moment(dt).format(I18n.t("dates.long_date_without_year"));
  }
}

export function updateRelativeAge(elems) {
  // jQuery .each
  elems.each(function () {
    const $this = $(this);
    $this.html(
      relativeAge(new Date($this.data("time")), {
        format: $this.data("format"),
        wrapInSpan: false,
      })
    );
  });
}

export function autoUpdatingRelativeAge(date, options) {
  if (!date) {
    return "";
  }
  if (+date === +new Date(0)) {
    return "";
  }

  options = options || {};
  let format = options.format || "tiny";

  let append = "";
  if (format === "medium") {
    append = " date";
    if (options.leaveAgo) {
      format = "medium-with-ago";
    }
    options.wrapInSpan = false;
  }

  const relAge = relativeAge(date, options);

  if (format === "tiny" && relativeAgeTinyShowsYear(relAge)) {
    append += " with-year";
  }

  if (options.title) {
    append += "' title='" + longDate(date);
  }

  return (
    "<span class='relative-date" +
    append +
    "' data-time='" +
    date.getTime() +
    "' data-format='" +
    format +
    "'>" +
    relAge +
    "</span>"
  );
}

function wrapAgo(dateStr) {
  return I18n.t("dates.wrap_ago", { date: dateStr });
}

export function durationTiny(distance, ageOpts) {
  if (typeof distance !== "number") {
    return "&mdash;";
  }

  const dividedDistance = Math.round(distance / 60.0);
  const distanceInMinutes = dividedDistance < 1 ? 1 : dividedDistance;

  const t = function (key, opts) {
    const result = I18n.t("dates.tiny." + key, opts);
    return ageOpts && ageOpts.addAgo ? wrapAgo(result) : result;
  };

  let formatted;

  switch (true) {
    case distance <= 59:
      formatted = t("less_than_x_minutes", { count: 1 });
      break;
    case distanceInMinutes >= 0 && distanceInMinutes <= 44:
      formatted = t("x_minutes", { count: distanceInMinutes });
      break;
    case distanceInMinutes >= 45 && distanceInMinutes <= 89:
      formatted = t("about_x_hours", { count: 1 });
      break;
    case distanceInMinutes >= 90 && distanceInMinutes <= 1409:
      formatted = t("about_x_hours", {
        count: Math.round(distanceInMinutes / 60.0),
      });
      break;
    case distanceInMinutes >= 1410 && distanceInMinutes <= 2519:
      formatted = t("x_days", { count: 1 });
      break;
    case distanceInMinutes >= 2520 && distanceInMinutes <= 129599:
      formatted = t("x_days", {
        count: Math.round(distanceInMinutes / 1440.0),
      });
      break;
    case distanceInMinutes >= 129600 && distanceInMinutes <= 525599:
      formatted = t("x_months", {
        count: Math.round(distanceInMinutes / 43200.0),
      });
      break;
    default:
      const numYears = distanceInMinutes / 525600.0;
      const remainder = numYears % 1;
      if (remainder < 0.25) {
        formatted = t("about_x_years", { count: Math.floor(numYears) });
      } else if (remainder < 0.75) {
        formatted = t("over_x_years", { count: Math.floor(numYears) });
      } else {
        formatted = t("almost_x_years", { count: Math.floor(numYears) + 1 });
      }

      break;
  }

  return formatted;
}

function relativeAgeTiny(date, ageOpts) {
  const format = "tiny";
  let distance = Math.round((new Date() - date) / 1000);
  if (distance < 0) {
    distance = Math.round((date - new Date()) / 1000);
  }
  const dividedDistance = Math.round(distance / 60.0);
  const distanceInMinutes = dividedDistance < 1 ? 1 : dividedDistance;

  let formatted;
  const t = function (key, opts) {
    const result = I18n.t("dates." + format + "." + key, opts);
    return ageOpts && ageOpts.addAgo ? wrapAgo(result) : result;
  };

  // This file is in lib but it's used as a helper
  let siteSettings = helperContext().siteSettings;

  switch (true) {
    case distanceInMinutes >= 0 && distanceInMinutes <= 44:
      formatted = t("x_minutes", { count: distanceInMinutes });
      break;
    case distanceInMinutes >= 45 && distanceInMinutes <= 89:
      formatted = t("about_x_hours", { count: 1 });
      break;
    case distanceInMinutes >= 90 && distanceInMinutes <= 1409:
      formatted = t("about_x_hours", {
        count: Math.round(distanceInMinutes / 60.0),
      });
      break;
    case siteSettings.relative_date_duration === 0 &&
      distanceInMinutes <= 525599:
      formatted = shortDateNoYear(date);
      break;
    case distanceInMinutes >= 1410 && distanceInMinutes <= 2519:
      formatted = t("x_days", { count: 1 });
      break;
    case distanceInMinutes >= 2520 &&
      distanceInMinutes <= (siteSettings.relative_date_duration || 14) * 1440:
      formatted = t("x_days", {
        count: Math.round(distanceInMinutes / 1440.0),
      });
      break;
    default:
      formatted = (ageOpts.defaultFormat || smartShortDate)(date);
      break;
  }

  return formatted;
}

/*
 * Returns true if the given tiny date string includes the year.
 * Useful for checking if the string isn't so tiny.
 */
function relativeAgeTinyShowsYear(relativeAgeString) {
  return relativeAgeString.match(/'[\d]{2}$/);
}

export function relativeAgeMediumSpan(distance, leaveAgo) {
  let formatted;
  const distanceInMinutes = Math.round(distance / 60.0);

  const t = function (key, opts) {
    return I18n.t(
      "dates.medium" + (leaveAgo ? "_with_ago" : "") + "." + key,
      opts
    );
  };

  switch (true) {
    case distanceInMinutes >= 1 && distanceInMinutes <= 55:
      formatted = t("x_minutes", { count: distanceInMinutes });
      break;
    case distanceInMinutes >= 56 && distanceInMinutes <= 89:
      formatted = t("x_hours", { count: 1 });
      break;
    case distanceInMinutes >= 90 && distanceInMinutes <= 1409:
      formatted = t("x_hours", { count: Math.round(distanceInMinutes / 60.0) });
      break;
    case distanceInMinutes >= 1410 && distanceInMinutes <= 2519:
      formatted = t("x_days", { count: 1 });
      break;
    case distanceInMinutes >= 2520 && distanceInMinutes <= 129599:
      formatted = t("x_days", {
        count: Math.round((distanceInMinutes - 720.0) / 1440.0),
      });
      break;
    case distanceInMinutes >= 129600 && distanceInMinutes <= 525599:
      formatted = t("x_months", {
        count: Math.round(distanceInMinutes / 43200.0),
      });
      break;
    default:
      formatted = t("x_years", {
        count: Math.round(distanceInMinutes / 525600.0),
      });
      break;
  }
  return formatted || "&mdash;";
}

function relativeAgeMedium(date, options) {
  const wrapInSpan = options.wrapInSpan !== false;
  const leaveAgo = options.leaveAgo;
  const distance = Math.round((new Date() - date) / 1000);

  if (!date) {
    return "&mdash;";
  }

  const fullReadable = longDate(date);
  const fiveDaysAgo = 432000;
  const oneMinuteAgo = 60;

  let displayDate = "";
  if (distance < oneMinuteAgo) {
    displayDate = I18n.t("now");
  } else if (distance > fiveDaysAgo) {
    displayDate = smartShortDate(date, shortDate);
  } else {
    displayDate = relativeAgeMediumSpan(distance, leaveAgo);
  }
  if (wrapInSpan) {
    return (
      "<span class='date' title='" +
      fullReadable +
      "'>" +
      displayDate +
      "</span>"
    );
  } else {
    return displayDate;
  }
}

// mostly lifted from rails with a few amendments
export function relativeAge(date, options) {
  options = options || {};
  const format = options.format || "tiny";

  if (format === "tiny") {
    return relativeAgeTiny(date, options);
  } else if (format === "medium") {
    return relativeAgeMedium(date, options);
  } else if (format === "medium-with-ago") {
    return relativeAgeMedium(
      date,
      Object.assign(options, { format: "medium", leaveAgo: true })
    );
  }

  return "UNKNOWN FORMAT";
}

export function number(val) {
  let formattedNumber;

  val = Math.round(parseFloat(val));
  if (isNaN(val)) {
    val = 0;
  }

  if (val > 999999) {
    formattedNumber = I18n.toNumber(val / 1000000, { precision: 1 });
    return I18n.t("number.short.millions", { number: formattedNumber });
  } else if (val > 99999) {
    formattedNumber = I18n.toNumber(Math.floor(val / 1000), { precision: 0 });
    return I18n.t("number.short.thousands", { number: formattedNumber });
  } else if (val > 999) {
    formattedNumber = I18n.toNumber(val / 1000, { precision: 1 });
    return I18n.t("number.short.thousands", { number: formattedNumber });
  }
  return val.toString();
}

export function ensureJSON(json) {
  return typeof json === "string" ? JSON.parse(json) : json;
}

export function plainJSON(val) {
  let json = ensureJSON(val);
  let headers = "";
  Object.keys(json).forEach((k) => {
    headers += `${k}: ${json[k]}\n`;
  });
  return headers;
}

export function prettyJSON(json) {
  return JSON.stringify(ensureJSON(json), null, 2);
}
