// @flow

import type { Repository, File } from "@scm-manager/ui-types";
import configureMockStore from "redux-mock-store";
import thunk from "redux-thunk";
import fetchMock from "fetch-mock";
import {
  FETCH_SOURCES,
  FETCH_SOURCES_FAILURE,
  FETCH_SOURCES_PENDING,
  FETCH_SOURCES_SUCCESS,
  fetchSources,
  getFetchSourcesFailure,
  isFetchSourcesPending,
  default as reducer,
  getSources,
  fetchSourcesSuccess,
  isDirectory
} from "./sources";

const sourcesUrl =
  "http://localhost:8081/scm/rest/api/v2/repositories/scm/core/sources/";

const repository: Repository = {
  name: "core",
  namespace: "scm",
  type: "git",
  _links: {
    sources: {
      href: sourcesUrl
    }
  }
};

const collection = {
  name: "src",
  path: "src",
  directory: true,
  description: "foo",
  length: 176,
  revision: "76aae4bb4ceacf0e88938eb5b6832738b7d537b4",
  subRepository: undefined,
  _links: {
    self: {
      href:
        "http://localhost:8081/scm/rest/api/v2/repositories/scm/core/sources/76aae4bb4ceacf0e88938eb5b6832738b7d537b4/"
    }
  },
  _embedded: {
    children: [
      {
        name: "src",
        path: "src",
        directory: true,
        description: "",
        length: 176,
        revision: "76aae4bb4ceacf0e88938eb5b6832738b7d537b4",
        lastModified: "",
        subRepository: undefined,
        _links: {
          self: {
            href:
              "http://localhost:8081/scm/rest/api/v2/repositories/scm/core/sources/76aae4bb4ceacf0e88938eb5b6832738b7d537b4/src"
          }
        },
        _embedded: {
          children: []
        }
      },
      {
        name: "package.json",
        path: "package.json",
        directory: false,
        description: "bump version",
        length: 780,
        revision: "76aae4bb4ceacf0e88938eb5b6832738b7d537b4",
        lastModified: "2017-07-31T11:17:19Z",
        subRepository: undefined,
        _links: {
          self: {
            href:
              "http://localhost:8081/scm/rest/api/v2/repositories/scm/core/content/76aae4bb4ceacf0e88938eb5b6832738b7d537b4/package.json"
          },
          history: {
            href:
              "http://localhost:8081/scm/rest/api/v2/repositories/scm/core/sources/history/76aae4bb4ceacf0e88938eb5b6832738b7d537b4/package.json"
          }
        },
        _embedded: {
          children: []
        }
      }
    ]
  }
};

const noDirectory: File = {
  name: "src",
  path: "src",
  directory: true,
  length: 176,
  revision: "abc",
  _links: {
    self: {
      href:
        "http://localhost:8081/scm/rest/api/v2/repositories/scm/core/sources/76aae4bb4ceacf0e88938eb5b6832738b7d537b4/src"
    }
  },
  _embedded: {
    children: []
  }
};

describe("sources fetch", () => {
  const mockStore = configureMockStore([thunk]);

  afterEach(() => {
    fetchMock.reset();
    fetchMock.restore();
  });

  it("should fetch the sources of the repository", () => {
    fetchMock.getOnce(sourcesUrl, collection);

    const expectedActions = [
      { type: FETCH_SOURCES_PENDING, itemId: "scm/core/_/" },
      {
        type: FETCH_SOURCES_SUCCESS,
        itemId: "scm/core/_/",
        payload: collection
      }
    ];

    const store = mockStore({});
    return store.dispatch(fetchSources(repository, "", "")).then(() => {
      expect(store.getActions()).toEqual(expectedActions);
    });
  });

  it("should fetch the sources of the repository with the given revision and path", () => {
    fetchMock.getOnce(sourcesUrl + "abc/src", collection);

    const expectedActions = [
      { type: FETCH_SOURCES_PENDING, itemId: "scm/core/abc/src" },
      {
        type: FETCH_SOURCES_SUCCESS,
        itemId: "scm/core/abc/src",
        payload: collection
      }
    ];

    const store = mockStore({});
    return store.dispatch(fetchSources(repository, "abc", "src")).then(() => {
      expect(store.getActions()).toEqual(expectedActions);
    });
  });

  it("should dispatch FETCH_SOURCES_FAILURE on server error", () => {
    fetchMock.getOnce(sourcesUrl, {
      status: 500
    });

    const store = mockStore({});
    return store.dispatch(fetchSources(repository, "", "")).then(() => {
      const actions = store.getActions();
      expect(actions[0].type).toBe(FETCH_SOURCES_PENDING);
      expect(actions[1].type).toBe(FETCH_SOURCES_FAILURE);
      expect(actions[1].itemId).toBe("scm/core/_/");
      expect(actions[1].payload).toBeDefined();
    });
  });
});

describe("reducer tests", () => {
  it("should return unmodified state on unknown action", () => {
    const state = {};
    expect(reducer(state)).toBe(state);
  });

  it("should store the collection, without revision and path", () => {
    const expectedState = {
      "scm/core/_/": collection
    };
    expect(
      reducer({}, fetchSourcesSuccess(repository, "", "", collection))
    ).toEqual(expectedState);
  });

  it("should store the collection, with revision and path", () => {
    const expectedState = {
      "scm/core/abc/src/main": collection
    };
    expect(
      reducer(
        {},
        fetchSourcesSuccess(repository, "abc", "src/main", collection)
      )
    ).toEqual(expectedState);
  });
});

describe("selector tests", () => {
  it("should return false if it is no directory", () => {
    const state = {
      sources: {
        "scm/core/abc/src/main/package.json": {
          noDirectory
        }
      }
    };
    expect(
      isDirectory(state, repository, "abc", "src/main/package.json")
    ).toBeFalsy();
  });

  it("should return true if it is directory", () => {
    const state = {
      sources: {
        "scm/core/abc/src": noDirectory
      }
    };
    expect(isDirectory(state, repository, "abc", "src")).toBe(true);
  });

  it("should return null", () => {
    expect(getSources({}, repository, "", "")).toBeFalsy();
  });

  it("should return the source collection without revision and path", () => {
    const state = {
      sources: {
        "scm/core/_/": collection
      }
    };
    expect(getSources(state, repository, "", "")).toBe(collection);
  });

  it("should return the source collection with revision and path", () => {
    const state = {
      sources: {
        "scm/core/abc/src/main": collection
      }
    };
    expect(getSources(state, repository, "abc", "src/main")).toBe(collection);
  });

  it("should return true, when fetch sources is pending", () => {
    const state = {
      pending: {
        [FETCH_SOURCES + "/scm/core/_/"]: true
      }
    };
    expect(isFetchSourcesPending(state, repository, "", "")).toEqual(true);
  });

  it("should return false, when fetch sources is not pending", () => {
    expect(isFetchSourcesPending({}, repository, "", "")).toEqual(false);
  });

  const error = new Error("incredible error from hell");

  it("should return error when fetch sources did fail", () => {
    const state = {
      failure: {
        [FETCH_SOURCES + "/scm/core/_/"]: error
      }
    };
    expect(getFetchSourcesFailure(state, repository, "", "")).toEqual(error);
  });

  it("should return undefined when fetch sources did not fail", () => {
    expect(getFetchSourcesFailure({}, repository, "", "")).toBe(undefined);
  });
});
