import * as assert from "assert";
import {
  mimeOrDefault,
  asset,
  Releaser,
  Release,
  findTagFromReleases,
} from "../src/github";

describe("github", () => {
  describe("mimeOrDefault", () => {
    it("returns a specific mime for common path", async () => {
      assert.equal(mimeOrDefault("foo.tar.gz"), "application/gzip");
    });
    it("returns default mime for uncommon path", async () => {
      assert.equal(mimeOrDefault("foo.uncommon"), "application/octet-stream");
    });
  });

  describe("asset", () => {
    it("derives asset info from a path", async () => {
      const { name, mime, size } = asset("tests/data/foo/bar.txt");
      assert.equal(name, "bar.txt");
      assert.equal(mime, "text/plain");
      assert.equal(size, 10);
    });
  });

  describe("findTagFromReleases", () => {
    const owner = "owner";
    const repo = "repo";

    const mockRelease: Release = {
      id: 1,
      upload_url: `https://api.github.com/repos/${owner}/${repo}/releases/1/assets`,
      html_url: `https://github.com/${owner}/${repo}/releases/tag/v1.0.0`,
      tag_name: "v1.0.0",
      name: "Test Release",
      body: "Test body",
      target_commitish: "main",
      draft: false,
      prerelease: false,
      assets: [],
    } as const;

    const mockReleaser: Releaser = {
      getReleaseByTag: () => Promise.reject("Not implemented"),
      createRelease: () => Promise.reject("Not implemented"),
      updateRelease: () => Promise.reject("Not implemented"),
      allReleases: async function* () {
        yield { data: [mockRelease] };
      },
    } as const;

    describe("when the tag_name is not an empty string", () => {
      const targetTag = "v1.0.0";

      it("finds a matching release in first batch of results", async () => {
        const targetRelease = {
          ...mockRelease,
          owner,
          repo,
          tag_name: targetTag,
        };
        const otherRelease = {
          ...mockRelease,
          owner,
          repo,
          tag_name: "v1.0.1",
        };

        const releaser = {
          ...mockReleaser,
          allReleases: async function* () {
            yield { data: [targetRelease] };
            yield { data: [otherRelease] };
          },
        };

        const result = await findTagFromReleases(
          releaser,
          owner,
          repo,
          targetTag,
        );

        assert.deepStrictEqual(result, targetRelease);
      });

      it("finds a matching release in second batch of results", async () => {
        const targetRelease = {
          ...mockRelease,
          owner,
          repo,
          tag_name: targetTag,
        };
        const otherRelease = {
          ...mockRelease,
          owner,
          repo,
          tag_name: "v1.0.1",
        };

        const releaser = {
          ...mockReleaser,
          allReleases: async function* () {
            yield { data: [otherRelease] };
            yield { data: [targetRelease] };
          },
        };

        const result = await findTagFromReleases(
          releaser,
          owner,
          repo,
          targetTag,
        );
        assert.deepStrictEqual(result, targetRelease);
      });

      it("returns undefined when a release is not found in any batch", async () => {
        const otherRelease = {
          ...mockRelease,
          owner,
          repo,
          tag_name: "v1.0.1",
        };
        const releaser = {
          ...mockReleaser,
          allReleases: async function* () {
            yield { data: [otherRelease] };
            yield { data: [otherRelease] };
          },
        };

        const result = await findTagFromReleases(
          releaser,
          owner,
          repo,
          targetTag,
        );

        assert.strictEqual(result, undefined);
      });

      it("returns undefined when no releases are returned", async () => {
        const releaser = {
          ...mockReleaser,
          allReleases: async function* () {
            yield { data: [] };
          },
        };

        const result = await findTagFromReleases(
          releaser,
          owner,
          repo,
          targetTag,
        );

        assert.strictEqual(result, undefined);
      });
    });

    describe("when the tag_name is an empty string", () => {
      const emptyTag = "";

      it("finds a matching release in first batch of results", async () => {
        const targetRelease = {
          ...mockRelease,
          owner,
          repo,
          tag_name: emptyTag,
        };
        const otherRelease = {
          ...mockRelease,
          owner,
          repo,
          tag_name: "v1.0.1",
        };

        const releaser = {
          ...mockReleaser,
          allReleases: async function* () {
            yield { data: [targetRelease] };
            yield { data: [otherRelease] };
          },
        };

        const result = await findTagFromReleases(
          releaser,
          owner,
          repo,
          emptyTag,
        );

        assert.deepStrictEqual(result, targetRelease);
      });

      it("finds a matching release in second batch of results", async () => {
        const targetRelease = {
          ...mockRelease,
          owner,
          repo,
          tag_name: emptyTag,
        };
        const otherRelease = {
          ...mockRelease,
          owner,
          repo,
          tag_name: "v1.0.1",
        };

        const releaser = {
          ...mockReleaser,
          allReleases: async function* () {
            yield { data: [otherRelease] };
            yield { data: [targetRelease] };
          },
        };

        const result = await findTagFromReleases(
          releaser,
          owner,
          repo,
          emptyTag,
        );
        assert.deepStrictEqual(result, targetRelease);
      });

      it("returns undefined when a release is not found in any batch", async () => {
        const otherRelease = {
          ...mockRelease,
          owner,
          repo,
          tag_name: "v1.0.1",
        };
        const releaser = {
          ...mockReleaser,
          allReleases: async function* () {
            yield { data: [otherRelease] };
            yield { data: [otherRelease] };
          },
        };

        const result = await findTagFromReleases(
          releaser,
          owner,
          repo,
          emptyTag,
        );

        assert.strictEqual(result, undefined);
      });

      it("returns undefined when no releases are returned", async () => {
        const releaser = {
          ...mockReleaser,
          allReleases: async function* () {
            yield { data: [] };
          },
        };

        const result = await findTagFromReleases(
          releaser,
          owner,
          repo,
          emptyTag,
        );

        assert.strictEqual(result, undefined);
      });
    });
  });
});