import DatetimeAttributeModel from "../DatetimeAttributeModel";
import {
  ATTRIBUTE_WIDTH,
  INCLUDE_TIME_OFFSET,
  setSettings,
} from "../../../constants";
import { DateTimeUtil } from "../../../utils";

describe("datetimeAttributeModel with offset on", () => {
  beforeEach(() => {
    setSettings({ [INCLUDE_TIME_OFFSET]: true });
  });

  it("should be able to create an empty DatetimeAttribute object", () => {
    const attribute = new DatetimeAttributeModel({}, { type: "datetime" });

    expect(attribute).toBeInstanceOf(DatetimeAttributeModel);

    expect(attribute.type).toBe("datetime");
    expect(attribute.format).toBe("yyyy-MM-dd'T'HH:mm:ssxxx");
    expect(attribute.getInputValue()).toBe("");
    expect(attribute.inputvalue).toBe("");
    expect(attribute.readonlyvalue).toBe("");

    attribute.addConstraints();
    expect(attribute.constraintCollection).toHaveLength(1);
  });

  it("should be able to update", () => {
    const attribute = new DatetimeAttributeModel(
      {},
      {
        type: "datetime",
        format: "dd-MM-yyyy HH:mm",
      },
    );

    attribute.update("18-08-2016 13:45 +02:00");
    expect(attribute.getInputValue()).toBe("18-08-2016 13:45 +02:00");
    expect(attribute.getInitialInputValue("2016-08-18T13:45:23+02:00")).toBe(
      "18-08-2016 13:45 +02:00",
    );

    expect(attribute.readonlyvalue).toBe("18-08-2016 13:45");

    attribute.update(null);
    expect(attribute.getInputValue()).toBe("");

    attribute.update("");
    expect(attribute.value).toBeNull();

    attribute.update("aaaa");
    expect(attribute.value).toBe("Invalid Date");
  });

  it("should be able to handle min and max date in only date format and renders in format", () => {
    const attribute = new DatetimeAttributeModel(
      {},
      {
        type: "datetime",
        format: "dd-MM-yyyy HH:mm",
        mindate: "2010-10-01T00:00:00+02:00", // CEST
        maxdate: "2010-10-31T23:59:59+01:00", // CET
      },
    );

    attribute.update("18-08-2016 13:45");

    expect(attribute.isValid).toBe(false);
    expect(attribute.formatValue(attribute.mindate)).toBe("01-10-2010 00:00");
    expect(attribute.formatValue(attribute.maxdate)).toBe("31-10-2010 23:59");
  });

  it("should return AttributeWidth for dates", () => {
    const attribute = new DatetimeAttributeModel(
      {},
      {
        type: "date",
      },
    );
    expect(attribute.readonlyWidth).toBe(ATTRIBUTE_WIDTH.SMALL);
  });

  it("should return AttributeWidth for times", () => {
    const attribute = new DatetimeAttributeModel(
      {},
      {
        type: "time",
      },
    );
    expect(attribute.readonlyWidth).toBe(ATTRIBUTE_WIDTH.SMALL);
  });

  it("should return AttributeWidth for datetime", () => {
    const attribute = new DatetimeAttributeModel(
      {},
      {
        type: "datetime",
      },
    );
    expect(attribute.readonlyWidth).toBe(ATTRIBUTE_WIDTH.MEDIUM);
  });

  it("can validate datetime with datetime format", () => {
    const datetimeFormat = new DatetimeAttributeModel(
      {},
      {
        type: "datetime",
        format: "dd-MM-yyyy HH:mm",
      },
    );

    expect(datetimeFormat.validate("19-10-2010 1:45 +02:00")).toBe(true);
    expect(datetimeFormat.validate("19-10-2010 13:45 +02:00")).toBe(true);

    expect(datetimeFormat.validate("19-10-2010")).toBe(false);
    expect(datetimeFormat.validate("13:45")).toBe(false);
    expect(datetimeFormat.validate("19-10-2010 1:45 am")).toBe(false);
  });

  it("can validate datetime with only date format", () => {
    const dateFormat = new DatetimeAttributeModel(
      {},
      {
        type: "datetime",
        format: "dd-MM-yyyy",
      },
    );

    expect(dateFormat.validate("19-10-2010")).toBe(true);

    expect(dateFormat.validate("19-10-2010 1:45")).toBe(false);
    expect(dateFormat.validate("13:45")).toBe(false);
    expect(dateFormat.validate("19-10-2010 1:45 am")).toBe(false);
  });

  it("can validate datetime with datetime am/pm format", () => {
    const datetimeAmPmFormat = new DatetimeAttributeModel(
      {},
      {
        type: "datetime",
        format: "dd-MM-yyyy hh:mm a",
      },
    );

    expect(datetimeAmPmFormat.validate("19-10-2010 1:45 am +02:00")).toBe(true);
    expect(datetimeAmPmFormat.validate("19-10-2010 1:45 pm +02:00")).toBe(true);

    expect(datetimeAmPmFormat.validate("19-10-2010 1:45 +02:00")).toBe(false);
    expect(datetimeAmPmFormat.validate("19-10-2010 13:45 +02:00")).toBe(false);
  });

  it("can handle old datetime format with ms", () => {
    const attribute = new DatetimeAttributeModel(
      { value: "2031-12-21T17:41:21.000+01:00" },
      {
        type: "datetime",
        format: "dd-MM-yyyy HH:mm",
      },
    );

    expect(attribute.readonlyvalue).toBe("21-12-2031 17:41");
  });

  it("returns date part of formatlabel as placeholder if attribute has date and time", () => {
    const attribute = new DatetimeAttributeModel(
      {},
      {
        type: "datetime",
        format: "dd-MM-yyyy HH:mm",
        formatlabel: "dd-MM-yyyy HH:mm",
      },
    );

    expect(attribute.placeholder).toBe("dd-MM-yyyy");
  });

  it("contains offset in formdata property", () => {
    const attribute = new DatetimeAttributeModel(
      { key: "datetime", value: "2031-12-21T17:41:21+01:00" },
      { type: "datetime" },
    );

    expect(attribute.formdata).toEqual({
      datetime: "2031-12-21T17:41:21+01:00",
    });
  });

  it("handles offsets and indicates ambiguouty", () => {
    const winter = new DatetimeAttributeModel(
      { key: "datetime", value: "2031-12-21T17:41:21+01:00" },
      { type: "datetime" },
    );

    expect(winter.isAmbiguous).toBe(false);
    expect(winter.offset).toEqual({
      abbr: "CET",
      label: "Central European Standard Time",
      value: "+01:00",
    });

    const summer = new DatetimeAttributeModel(
      { key: "datetime", value: "2031-07-01T17:41:21+02:00" },
      { type: "datetime" },
    );

    expect(summer.isAmbiguous).toBe(false);
    expect(summer.offset).toEqual({
      abbr: "CEST",
      label: "Central European Summer Time",
      value: "+02:00",
    });

    const ambiguousSummer = new DatetimeAttributeModel(
      { key: "datetime", value: "2024-10-27T02:30:00+02:00" },
      { type: "datetime" },
    );

    expect(ambiguousSummer.isAmbiguous).toBe(true);
    expect(ambiguousSummer.offset).toEqual({
      abbr: "CEST",
      label: "Central European Summer Time",
      value: "+02:00",
    });

    const ambiguousWinter = new DatetimeAttributeModel(
      { key: "datetime", value: "2024-10-27T02:30:00+01:00" },
      { type: "datetime" },
    );

    expect(ambiguousWinter.isAmbiguous).toBe(true);
    expect(ambiguousWinter.offset).toEqual({
      abbr: "CET",
      label: "Central European Standard Time",
      value: "+01:00",
    });
  });

  it("retrieves the correct error collection", () => {
    const attribute = new DatetimeAttributeModel(
      {},
      {
        type: "datetime",
        format: "dd-MM-yyyy HH:mm",
      },
    );

    attribute.inputvalue = "27-10-2024";

    expect(attribute.errorCollection.map((error) => error.id)).toEqual([
      "Constraint.DateTime.MissingValue",
    ]);

    attribute.inputvalue = "27-10-2024 02:30";

    expect(attribute.errorCollection.map((error) => error.id)).toEqual([
      "Constraint.DateTime.MissingOffset",
    ]);

    attribute.inputvalue = "27-10-2024 +02:00";

    expect(attribute.errorCollection.map((error) => error.id)).toEqual([
      "Constraint.DateTime.MissingValue",
    ]);
  });

  it("handles dst", () => {
    const attribute1 = new DatetimeAttributeModel(
      { value: "2024-10-27T01:30:00+00:00" },
      {
        type: "datetime",
        format: "dd-MM-yyyy HH:mm",
      },
    );

    expect(attribute1.initvalue).toBe("2024-10-27T02:30:00+01:00");
    expect(
      DateTimeUtil.toFormat(attribute1.value, "dd-MM-yyyy HH:mm xxx"),
    ).toBe("27-10-2024 02:30 +01:00");
    expect(attribute1.isAmbiguous).toBe(true);
    expect(attribute1.offset).toEqual({
      abbr: "CET",
      label: "Central European Standard Time",
      value: "+01:00",
    });

    const attribute2 = new DatetimeAttributeModel(
      { value: "2024-10-27T00:30:00+00:00" },
      {
        type: "datetime",
        format: "dd-MM-yyyy HH:mm",
      },
    );

    expect(attribute2.initvalue).toBe("2024-10-27T02:30:00+02:00");
    expect(
      DateTimeUtil.toFormat(attribute2.value, "dd-MM-yyyy HH:mm xxx"),
    ).toBe("27-10-2024 02:30 +02:00");
    expect(attribute2.isAmbiguous).toBe(true);
    expect(attribute2.offset).toEqual({
      abbr: "CEST",
      label: "Central European Summer Time",
      value: "+02:00",
    });
  });

  it("Date formats", () => {
    const datetime = new DatetimeAttributeModel(
      { key: "datetime", value: "1969-09-23T12:34:00+00:00" },
      {
        type: "datetime",
        format: "dd-MM-yyyy HH:mm X",
      },
    );

    expect(datetime.format).toBe("dd-MM-yyyy HH:mm X");
    expect(datetime.dateInputFormat).toBe("dd-MM-yyyy");
    expect(datetime.timeInputFormat).toBe("HH:mm");
    expect(datetime.errorCollection.isEmpty).toBe(true);

    const timestamp = new DatetimeAttributeModel(
      { key: "timestamp", value: "1969-09-23T12:34:00.010+00:00" },
      {
        type: "timestamp",
        format: "dd-MM-yyyy HH:mm:ss.SSS X",
      },
    );

    expect(timestamp.format).toBe("dd-MM-yyyy HH:mm:ss.SSS X");
    expect(timestamp.dateInputFormat).toBe("dd-MM-yyyy");
    expect(timestamp.timeInputFormat).toBe("HH:mm:ss.SSS");

    const special = new DatetimeAttributeModel(
      { key: "timestamp", value: "1969-09-23T12:34:00.010+00:00" },
      {
        type: "timestamp",
        format: "'special' do 'de' MMMM yyyy h 'o''clock'",
      },
    );

    expect(special.format).toBe("'special' do 'de' MMMM yyyy h 'o''clock'");
    expect(special.dateInputFormat).toBe("do 'de' MMMM yyyy");
    // expect(special.timeInputFormat).toBe("HH:mm:ss.SSS");
  });
});
