import { EnoFactory } from "./EnoFactory";
import { IEnSrvOptions } from "./IEnSrvOptions";
import * as Read from "./read";
import { firstValueFrom, of, throwError } from "rxjs";
import { Eno } from "./models/Eno";
import { getLangs, ILocale, locale, nowVar } from "./locale";

describe("locale", () => {
  let testLocaleEno: Eno;
  let testLocale: ILocale;
  let testEnSrvOptions: IEnSrvOptions;
  let readSpy: jasmine.Spy;

  beforeEach(() => {
    testEnSrvOptions = {
      enSrvUrl: "http://example.com",
      namespace: "myNameSpace",
    };
    const enoFactory = new EnoFactory("app/type/locale");
    enoFactory.setSecurity("security/policy/everyone");
    enoFactory.setField("app/type/locale:locale-id", ["fr-fr"]);
    enoFactory.setField("app/type/locale:timezone", ["Australia/Sydney"]);
    enoFactory.setField("app/type/locale:dateformat", ["DD MMM YYYY"]);
    enoFactory.setField("app/type/locale:datetimeformat", [
      "DD MMM YYYY HH:mm",
    ]);
    testLocaleEno = enoFactory.makeEno();
    testLocale = {
      lang: "fr-fr",
      timezone: "Australia/Sydney",
      dateFormat: "DD MMM YYYY",
      datetimeFormat: "DD MMM YYYY HH:mm",
    };
    readSpy = spyOn(Read, "read");
  });

  it("Should get the locale", async () => {
    readSpy.and.returnValue(of(testLocaleEno));
    expect(testEnSrvOptions.locale).toBeUndefined();

    const result = await firstValueFrom(locale(testEnSrvOptions));

    expect(readSpy).toHaveBeenCalledWith("app/locale", testEnSrvOptions);
    expect(result).toEqual(testLocale);
    expect(testEnSrvOptions.locale).toEqual(testLocale);
  });

  it("Should store and get the locale from cache", async () => {
    readSpy.and.returnValue(of(testLocaleEno));
    expect(testEnSrvOptions.locale).toBeUndefined();

    const result1 = await firstValueFrom(locale(testEnSrvOptions));

    expect(readSpy).toHaveBeenCalledTimes(1);
    expect(result1).toEqual(testLocale);
    expect(testEnSrvOptions.locale).toEqual(testLocale);

    const result2 = await firstValueFrom(locale(testEnSrvOptions));

    expect(readSpy).toHaveBeenCalledTimes(1);
    expect(result2).toEqual(testLocale);
  });

  it("Should error when not found and not in cache", (done) => {
    readSpy.and.returnValue(throwError(() => new Error("Deliberate error")));

    locale(testEnSrvOptions).subscribe({
      next: fail,
      error: (error) => {
        expect(error.message).toBe("Deliberate error");
        expect(readSpy).toHaveBeenCalled();
        done();
      },
    });
  });

  it("Should get the now variable", async () => {
    readSpy.and.returnValue(of(testLocaleEno));

    const result = await firstValueFrom(nowVar(testEnSrvOptions));

    expect(result).toMatch(
      /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\+[0-9]{4}$/
    );
  });

  it("Should get the now variable falling back to UTC", async () => {
    readSpy.and.returnValue(throwError(() => new Error("Deliberate error")));

    const result = await firstValueFrom(nowVar(testEnSrvOptions));

    expect(result).toMatch(
      /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\+0000$/
    );
  });

  it("Should get the langs", async () => {
    readSpy.and.returnValue(of(testLocaleEno));
    const result = await firstValueFrom(getLangs(testEnSrvOptions));
    expect(result).toEqual(["fr-fr", "en-us"]);
  });

  it("Should get fallback langs", async () => {
    readSpy.and.returnValue(throwError(() => new Error("Deliberate error")));
    const result = await firstValueFrom(getLangs(testEnSrvOptions));
    expect(result).toEqual(["en-us"]);
  });

  it("Should get the given lang as a string first", async () => {
    readSpy.and.returnValue(of(testLocaleEno));
    const result = await firstValueFrom(getLangs(testEnSrvOptions, "es-es"));
    expect(result).toEqual(["es-es", "fr-fr", "en-us"]);
  });

  it("Should get the given lang as an array first", async () => {
    readSpy.and.returnValue(of(testLocaleEno));
    const result = await firstValueFrom(
      getLangs(testEnSrvOptions, ["es-es", "de-de"])
    );
    expect(result).toEqual(["es-es", "de-de", "fr-fr", "en-us"]);
  });

  it("Should get the given lang first, then fallback langs", async () => {
    readSpy.and.returnValue(throwError(() => new Error("Deliberate error")));
    const result = await firstValueFrom(getLangs(testEnSrvOptions, "es-es"));
    expect(result).toEqual(["es-es", "en-us"]);
  });

  it("Should not repeat the fallback", async () => {
    readSpy.and.returnValue(throwError(() => new Error("Deliberate error")));
    const result = await firstValueFrom(getLangs(testEnSrvOptions, "en-us"));
    expect(result).toEqual(["en-us"]);
  });

  it("Should not repeat the system lang", async () => {
    readSpy.and.returnValue(of(testLocaleEno));
    const result = await firstValueFrom(getLangs(testEnSrvOptions, "fr-fr"));
    expect(result).toEqual(["fr-fr", "en-us"]);
  });

  it("Should move the fallback lang first if asked", async () => {
    readSpy.and.returnValue(of(testLocaleEno));
    const result = await firstValueFrom(getLangs(testEnSrvOptions, "en-us"));
    expect(result).toEqual(["en-us", "fr-fr"]);
  });

  it("Should not add the fallback lang", async () => {
    readSpy.and.returnValue(of(testLocaleEno));
    const result = await firstValueFrom(getLangs(testEnSrvOptions, "fr", false));
    expect(result).toEqual(["fr"]);
  });
});
