import zeroFill from "../utils/zero-fill.js";
import { createDuration } from "../duration/create.js";
import { addSubtract } from "../moment/add-subtract.js";
import { isMoment, copyConfig } from "../moment/constructor.js";
import { addFormatToken } from "../format/format.js";
import { addRegexToken, matchOffset, matchShortOffset } from "../parse/regex.js";
import { addParseToken } from "../parse/token.js";
import { createLocal } from "../create/local.js";
import { prepareConfig } from "../create/from-anything.js";
import { createUTC } from "../create/utc.js";
import isDate from "../utils/is-date.js";
import toInt from "../utils/to-int.js";
import isUndefined from "../utils/is-undefined.js";
import compareArrays from "../utils/compare-arrays.js";
import { hooks } from "../utils/hooks.js"; // FORMATTING

function offset(token, separator) {
  addFormatToken(token, 0, 0, function () {
    var offset = this.utcOffset(),
        sign = '+';

    if (offset < 0) {
      offset = -offset;
      sign = '-';
    }

    return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~offset % 60, 2);
  });
}

offset('Z', ':');
offset('ZZ', ''); // PARSING

addRegexToken('Z', matchShortOffset);
addRegexToken('ZZ', matchShortOffset);
addParseToken(['Z', 'ZZ'], function (input, array, config) {
  config._useUTC = true;
  config._tzm = offsetFromString(matchShortOffset, input);
}); // HELPERS
// timezone chunker
// '+10:00' > ['10',  '00']
// '-1530'  > ['-15', '30']

var chunkOffset = /([\+\-]|\d\d)/gi;

function offsetFromString(matcher, string) {
  var matches = (string || '').match(matcher),
      chunk,
      parts,
      minutes;

  if (matches === null) {
    return null;
  }

  chunk = matches[matches.length - 1] || [];
  parts = (chunk + '').match(chunkOffset) || ['-', 0, 0];
  minutes = +(parts[1] * 60) + toInt(parts[2]);
  return minutes === 0 ? 0 : parts[0] === '+' ? minutes : -minutes;
} // Return a moment from input, that is local/utc/zone equivalent to model.


export function cloneWithOffset(input, model) {
  var res, diff;

  if (model._isUTC) {
    res = model.clone();
    diff = (isMoment(input) || isDate(input) ? input.valueOf() : createLocal(input).valueOf()) - res.valueOf(); // Use low-level api, because this fn is low-level api.

    res._d.setTime(res._d.valueOf() + diff);

    hooks.updateOffset(res, false);
    return res;
  } else {
    return createLocal(input).local();
  }
}

function getDateOffset(m) {
  // On Firefox.24 Date#getTimezoneOffset returns a floating point.
  // https://github.com/moment/moment/pull/1871
  return -Math.round(m._d.getTimezoneOffset());
} // HOOKS
// This function will be called whenever a moment is mutated.
// It is intended to keep the offset in sync with the timezone.


hooks.updateOffset = function () {}; // MOMENTS
// keepLocalTime = true means only change the timezone, without
// affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]-->
// 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset
// +0200, so we adjust the time as needed, to be valid.
//
// Keeping the time actually adds/subtracts (one hour)
// from the actual represented time. That is why we call updateOffset
// a second time. In case it wants us to change the offset again
// _changeInProgress == true case, then we have to adjust, because
// there is no such time in the given timezone.


export function getSetOffset(input, keepLocalTime, keepMinutes) {
  var offset = this._offset || 0,
      localAdjust;

  if (!this.isValid()) {
    return input != null ? this : NaN;
  }

  if (input != null) {
    if (typeof input === 'string') {
      input = offsetFromString(matchShortOffset, input);

      if (input === null) {
        return this;
      }
    } else if (Math.abs(input) < 16 && !keepMinutes) {
      input = input * 60;
    }

    if (!this._isUTC && keepLocalTime) {
      localAdjust = getDateOffset(this);
    }

    this._offset = input;
    this._isUTC = true;

    if (localAdjust != null) {
      this.add(localAdjust, 'm');
    }

    if (offset !== input) {
      if (!keepLocalTime || this._changeInProgress) {
        addSubtract(this, createDuration(input - offset, 'm'), 1, false);
      } else if (!this._changeInProgress) {
        this._changeInProgress = true;
        hooks.updateOffset(this, true);
        this._changeInProgress = null;
      }
    }

    return this;
  } else {
    return this._isUTC ? offset : getDateOffset(this);
  }
}
export function getSetZone(input, keepLocalTime) {
  if (input != null) {
    if (typeof input !== 'string') {
      input = -input;
    }

    this.utcOffset(input, keepLocalTime);
    return this;
  } else {
    return -this.utcOffset();
  }
}
export function setOffsetToUTC(keepLocalTime) {
  return this.utcOffset(0, keepLocalTime);
}
export function setOffsetToLocal(keepLocalTime) {
  if (this._isUTC) {
    this.utcOffset(0, keepLocalTime);
    this._isUTC = false;

    if (keepLocalTime) {
      this.subtract(getDateOffset(this), 'm');
    }
  }

  return this;
}
export function setOffsetToParsedOffset() {
  if (this._tzm != null) {
    this.utcOffset(this._tzm, false, true);
  } else if (typeof this._i === 'string') {
    var tZone = offsetFromString(matchOffset, this._i);

    if (tZone != null) {
      this.utcOffset(tZone);
    } else {
      this.utcOffset(0, true);
    }
  }

  return this;
}
export function hasAlignedHourOffset(input) {
  if (!this.isValid()) {
    return false;
  }

  input = input ? createLocal(input).utcOffset() : 0;
  return (this.utcOffset() - input) % 60 === 0;
}
export function isDaylightSavingTime() {
  return this.utcOffset() > this.clone().month(0).utcOffset() || this.utcOffset() > this.clone().month(5).utcOffset();
}
export function isDaylightSavingTimeShifted() {
  if (!isUndefined(this._isDSTShifted)) {
    return this._isDSTShifted;
  }

  var c = {},
      other;
  copyConfig(c, this);
  c = prepareConfig(c);

  if (c._a) {
    other = c._isUTC ? createUTC(c._a) : createLocal(c._a);
    this._isDSTShifted = this.isValid() && compareArrays(c._a, other.toArray()) > 0;
  } else {
    this._isDSTShifted = false;
  }

  return this._isDSTShifted;
}
export function isLocal() {
  return this.isValid() ? !this._isUTC : false;
}
export function isUtcOffset() {
  return this.isValid() ? this._isUTC : false;
}
export function isUtc() {
  return this.isValid() ? this._isUTC && this._offset === 0 : false;
}