import * as utils from '../../../utils';
import * as api from './api';
import * as urls from './urls';
import { get, post, deleteObject } from './utils';

jest.mock('../../../utils', () => {
  const camelizeMap = (obj) => ({ ...obj, camelized: true });
  return {
    ...jest.requireActual('../../../utils'),
    camelize: camelizeMap,
    camelizeKeys: (list) => list.map(camelizeMap),
  };
});

jest.mock('./urls', () => ({
  block: jest.fn().mockName('urls.block'),
  blockAncestor: jest.fn().mockName('urls.blockAncestor'),
  blockStudioView: jest.fn().mockName('urls.StudioView'),
  courseAssets: jest.fn().mockName('urls.courseAssets'),
  videoTranscripts: jest.fn().mockName('urls.videoTranscripts'),
  allowThumbnailUpload: jest.fn().mockName('urls.allowThumbnailUpload'),
  thumbnailUpload: jest.fn().mockName('urls.thumbnailUpload'),
  checkTranscriptsForImport: jest.fn().mockName('urls.checkTranscriptsForImport'),
  replaceTranscript: jest.fn().mockName('urls.replaceTranscript'),
}));

jest.mock('./utils', () => ({
  get: jest.fn().mockName('get'),
  post: jest.fn().mockName('post'),
  deleteObject: jest.fn().mockName('deleteObject'),
}));

const { camelize } = utils;

const { apiMethods } = api;

const blockId = 'coursev1:2uX@4345432';
const learningContextId = 'demo2uX';
const studioEndpointUrl = 'hortus.coa';
const title = 'remember this needs to go into metadata to save';

describe('cms api', () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });
  describe('apiMethods', () => {
    describe('fetchBlockId', () => {
      it('should call get with url.blocks', () => {
        apiMethods.fetchBlockById({ blockId, studioEndpointUrl });
        expect(get).toHaveBeenCalledWith(urls.block({ blockId, studioEndpointUrl }));
      });
    });

    describe('fetchByUnitId', () => {
      it('should call get with url.blockAncestor', () => {
        apiMethods.fetchByUnitId({ blockId, studioEndpointUrl });
        expect(get).toHaveBeenCalledWith(urls.blockAncestor({ studioEndpointUrl, blockId }));
      });
    });

    describe('fetchStudioView', () => {
      it('should call get with url.blockStudioView', () => {
        apiMethods.fetchStudioView({ blockId, studioEndpointUrl });
        expect(get).toHaveBeenCalledWith(urls.blockStudioView({ studioEndpointUrl, blockId }));
      });
    });

    describe('fetchAssets', () => {
      it('should call get with url.courseAssets', () => {
        apiMethods.fetchAssets({ learningContextId, studioEndpointUrl });
        expect(get).toHaveBeenCalledWith(urls.courseAssets({ studioEndpointUrl, learningContextId }));
      });
    });

    describe('normalizeContent', () => {
      test('return value for blockType: html', () => {
        const content = 'Im baby palo santo ugh celiac fashion axe. La croix lo-fi venmo whatever. Beard man braid migas single-origin coffee forage ramps.';
        expect(apiMethods.normalizeContent({
          blockId,
          blockType: 'html',
          content,
          learningContextId,
          title,
        })).toEqual({
          category: 'html',
          courseKey: learningContextId,
          data: content,
          has_changes: true,
          id: blockId,
          metadata: { display_name: title },
        });
      });
      test('return value for blockType: video', () => {
        const content = {
          videoSource: 'viDeOSouRCE',
          fallbackVideos: 'FalLBacKVidEOs',
          allowVideoDownloads: 'alLOwViDeodownLOads',
          thumbnail: 'THUmbNaIL',
          transcripts: 'traNScRiPts',
          allowTranscriptDownloads: 'aLloWTRaNScriPtdoWnlOADS',
          duration: {
            startTime: '00:00:00',
            stopTime: '00:00:00',
          },
          showTranscriptByDefault: 'ShOWtrANscriPTBYDeFAulT',
          handout: 'HAnDOuT',
          licenseType: 'LiCeNsETYpe',
          licenseDetails: 'liCENSeDetAIls',
        };
        const html5Sources = 'hTML5souRCES';
        const edxVideoId = 'eDXviDEOid';
        const youtubeId = 'yOUtUBeid';
        const license = 'LiCEnsE';
        jest.spyOn(api, 'processVideoIds').mockReturnValue({
          html5Sources,
          edxVideoId,
          youtubeId,
        });
        jest.spyOn(api, 'processLicense').mockReturnValue(license);
        expect(apiMethods.normalizeContent({
          blockId,
          blockType: 'video',
          content,
          learningContextId,
          title,
        })).toEqual({
          category: 'video',
          courseKey: learningContextId,
          display_name: title,
          id: blockId,
          metadata: {
            display_name: title,
            download_video: content.allowVideoDownloads,
            edx_video_id: edxVideoId,
            html5_sources: html5Sources,
            youtube_id_1_0: youtubeId,
            thumbnail: content.thumbnail,
            download_track: content.allowTranscriptDownloads,
            track: '',
            show_captions: content.showTranscriptByDefault,
            handout: content.handout,
            start_time: content.duration.startTime,
            end_time: content.duration.stopTime,
            license,
          },
        });
        jest.restoreAllMocks();
      });
      test('throw error for invalid blockType', () => {
        expect(() => { apiMethods.normalizeContent({ blockType: 'somethingINVALID' }); })
          .toThrow(TypeError);
      });
    });

    describe('saveBlock', () => {
      const content = 'Im baby palo santo ugh celiac fashion axe. La croix lo-fi venmo whatever. Beard man braid migas single-origin coffee forage ramps.';
      it('should call post with urls.block and normalizeContent', () => {
        apiMethods.saveBlock({
          blockId,
          blockType: 'html',
          content,
          learningContextId,
          studioEndpointUrl,
          title,
        });
        expect(post).toHaveBeenCalledWith(
          urls.block({ studioEndpointUrl }),
          apiMethods.normalizeContent({
            blockType: 'html',
            content,
            blockId,
            learningContextId,
            title,
          }),
        );
      });
    });

    describe('uploadAsset', () => {
      const asset = { photo: 'dAta' };
      it('should call post with urls.courseAssets and imgdata', () => {
        const mockFormdata = new FormData();
        mockFormdata.append('file', asset);
        apiMethods.uploadAsset({
          learningContextId,
          studioEndpointUrl,
          asset,
        });
        expect(post).toHaveBeenCalledWith(
          urls.courseAssets({ studioEndpointUrl, learningContextId }),
          mockFormdata,
        );
      });
    });
  });
  describe('loadImage', () => {
    it('loads incoming image data, replacing the dateAdded with a date field', () => {
      const [date, time] = ['Jan 20, 2022', '9:30 PM'];
      const imageData = { some: 'image data', dateAdded: `${date} at ${time}` };
      expect(api.loadImage(imageData)).toEqual({
        ...imageData,
        dateAdded: new Date(`${date} ${time}`).getTime(),
      });
    });
  });
  describe('loadImages', () => {
    it('loads a list of images into an object by id, using loadImage to translate', () => {
      const ids = ['id0', 'Id1', 'ID2', 'iD3'];
      const testData = [
        { id: ids[0], some: 'data' },
        { id: ids[1], other: 'data' },
        { id: ids[2], some: 'DATA' },
        { id: ids[3], other: 'DATA' },
      ];
      const oldLoadImage = api.loadImage;
      api.loadImage = (imageData) => ({ loadImage: imageData });
      const out = api.loadImages(testData);
      expect(out).toEqual({
        [ids[0]]: api.loadImage(camelize(testData[0])),
        [ids[1]]: api.loadImage(camelize(testData[1])),
        [ids[2]]: api.loadImage(camelize(testData[2])),
        [ids[3]]: api.loadImage(camelize(testData[3])),
      });
      api.loadImage = oldLoadImage;
    });
  });
  describe('uploadThumbnail', () => {
    describe('uploadThumbnail', () => {
      const thumbnail = 'dAta';
      const videoId = 'sOmeVIDeoiD';
      it('should call post with urls.thumbnailUpload and thumbnail data', () => {
        const mockFormdata = new FormData();
        mockFormdata.append('file', thumbnail);
        apiMethods.uploadThumbnail({
          studioEndpointUrl,
          learningContextId,
          videoId,
          thumbnail,
        });
        expect(post).toHaveBeenCalledWith(
          urls.thumbnailUpload({ studioEndpointUrl, learningContextId, videoId }),
          mockFormdata,
        );
      });
    });
    describe('allowThumbnailUpload', () => {
      it('should call get with url.allowThumbnailUpload', () => {
        apiMethods.allowThumbnailUpload({ studioEndpointUrl });
        expect(get).toHaveBeenCalledWith(urls.allowThumbnailUpload({ studioEndpointUrl }));
      });
    });
  });
  describe('videoTranscripts', () => {
    const language = 'la';
    const videoId = 'sOmeVIDeoiD';
    const youTubeId = 'SOMeyoutUBeid';
    describe('checkTranscriptsForImport', () => {
      const getJSON = `{"locator":"${blockId}","videos":[{"mode":"youtube","video":"${youTubeId}","type":"youtube"},{"mode":"edx_video_id","type":"edx_video_id","video":"${videoId}"}]}`;
      it('should call get with url.checkTranscriptsForImport', () => {
        apiMethods.checkTranscriptsForImport({
          studioEndpointUrl,
          blockId,
          videoId,
          youTubeId,
        });
        expect(get).toHaveBeenCalledWith(urls.checkTranscriptsForImport({
          studioEndpointUrl,
          parameters: encodeURIComponent(getJSON),
        }));
      });
    });
    describe('importTranscript', () => {
      const getJSON = `{"locator":"${blockId}","videos":[{"mode":"youtube","video":"${youTubeId}","type":"youtube"}]}`;
      it('should call get with url.replaceTranscript', () => {
        apiMethods.importTranscript({ studioEndpointUrl, blockId, youTubeId });
        expect(get).toHaveBeenCalledWith(urls.replaceTranscript({
          studioEndpointUrl,
          parameters: encodeURIComponent(getJSON),
        }));
      });
    });
    describe('uploadTranscript', () => {
      const transcript = { transcript: 'dAta' };
      it('should call post with urls.videoTranscripts and transcript data', () => {
        const mockFormdata = new FormData();
        mockFormdata.append('file', transcript);
        mockFormdata.append('edx_video_id', videoId);
        mockFormdata.append('language_code', language);
        mockFormdata.append('new_language_code', language);
        apiMethods.uploadTranscript({
          blockId,
          studioEndpointUrl,
          transcript,
          videoId,
          language,
        });
        expect(post).toHaveBeenCalledWith(
          urls.videoTranscripts({ studioEndpointUrl, blockId }),
          mockFormdata,
        );
      });
    });
    describe('transcript delete', () => {
      it('should call deleteObject with urls.videoTranscripts and transcript data', () => {
        const mockDeleteJSON = { data: { lang: language, edx_video_id: videoId } };
        apiMethods.deleteTranscript({
          blockId,
          studioEndpointUrl,
          videoId,
          language,
        });
        expect(deleteObject).toHaveBeenCalledWith(
          urls.videoTranscripts({ studioEndpointUrl, blockId }),
          mockDeleteJSON,
        );
      });
    });
    describe('transcript get', () => {
      it('should call get with urls.videoTranscripts and transcript data', () => {
        const mockJSON = { data: { lang: language, edx_video_id: videoId } };
        apiMethods.getTranscript({
          blockId,
          studioEndpointUrl,
          videoId,
          language,
        });
        expect(get).toHaveBeenCalledWith(
          `${urls.videoTranscripts({ studioEndpointUrl, blockId })}?language_code=${language}`,
          mockJSON,
        );
      });
    });
  });
  describe('processVideoIds', () => {
    const edxVideoId = 'eDXviDEoid';
    const youtubeId = 'yOuTuBeUrL';
    const youtubeUrl = `https://youtu.be/${youtubeId}`;
    const html5Sources = [
      'sOuRce1',
      'sourCE2',
    ];
    afterEach(() => {
      jest.restoreAllMocks();
    });
    describe('if there is a video id', () => {
      beforeEach(() => {
        jest.spyOn(api, 'isEdxVideo').mockReturnValue(true);
        jest.spyOn(api, 'parseYoutubeId').mockReturnValue(youtubeId);
      });
      it('returns edxVideoId when there are no fallbackVideos', () => {
        expect(api.processVideoIds({
          videoUrl: '',
          fallbackVideos: [],
          videoId: edxVideoId,
        })).toEqual({
          edxVideoId,
          html5Sources: [],
          youtubeId: '',
        });
      });
      it('returns edxVideoId and html5Sources when there are fallbackVideos', () => {
        expect(api.processVideoIds({
          videoUrl: youtubeUrl,
          fallbackVideos: html5Sources,
          videoId: edxVideoId,
        })).toEqual({
          edxVideoId,
          html5Sources,
          youtubeId,
        });
      });
    });
    describe('if there is a youtube url', () => {
      beforeEach(() => {
        jest.spyOn(api, 'isEdxVideo').mockReturnValue(false);
        jest.spyOn(api, 'parseYoutubeId').mockReturnValue(youtubeId);
      });
      it('returns youtubeId when there are no fallbackVideos', () => {
        expect(api.processVideoIds({
          videoUrl: youtubeUrl,
          fallbackVideos: [],
          videoId: '',
        })).toEqual({
          edxVideoId: '',
          html5Sources: [],
          youtubeId,
        });
      });
      it('returns youtubeId and html5Sources when there are fallbackVideos', () => {
        expect(api.processVideoIds({
          videoUrl: youtubeUrl,
          fallbackVideos: html5Sources,
          videoId: '',
        })).toEqual({
          edxVideoId: '',
          html5Sources,
          youtubeId,
        });
      });
    });
    describe('if the videoSource is an html5 source', () => {
      beforeEach(() => {
        jest.spyOn(api, 'isEdxVideo').mockReturnValue(false);
        jest.spyOn(api, 'parseYoutubeId').mockReturnValue(null);
      });
      it('returns html5Sources when there are no fallbackVideos', () => {
        expect(api.processVideoIds({
          videoUrl: html5Sources[0],
          fallbackVideos: [],
          videoId: '',
        })).toEqual({
          edxVideoId: '',
          html5Sources: [html5Sources[0]],
          youtubeId: '',
        });
      });
      it('returns html5Sources when there are fallbackVideos', () => {
        expect(api.processVideoIds({
          videoUrl: html5Sources[0],
          fallbackVideos: [html5Sources[1]],
          videoId: '',
        })).toEqual({
          edxVideoId: '',
          html5Sources,
          youtubeId: '',
        });
      });
    });
  });
  describe('isEdxVideo', () => {
    it('returns true if id is in uuid4 format', () => {
      const id = 'c2afd8c8-3329-4dfc-95be-4ee6d986c3e5';
      expect(api.isEdxVideo(id)).toEqual(true);
    });
    it('returns false if id is not in uuid4 format', () => {
      const id = 'someB-ad-Id';
      expect(api.isEdxVideo(id)).toEqual(false);
    });
  });
  describe('parseYoutubeId', () => {
    it('returns the youtube id in an url', () => {
      const id = '3_yD_cEKoCk';
      const testURLs = [
        'https://www.youtube.com/watch?v=3_yD_cEKoCk&feature=featured',
        'https://www.youtube.com/watch?v=3_yD_cEKoCk',
        'http://www.youtube.com/watch?v=3_yD_cEKoCk',
        '//www.youtube.com/watch?v=3_yD_cEKoCk',
        'www.youtube.com/watch?v=3_yD_cEKoCk',
        'https://youtube.com/watch?v=3_yD_cEKoCk',
        'http://youtube.com/watch?v=3_yD_cEKoCk',
        '//youtube.com/watch?v=3_yD_cEKoCk',
        'youtube.com/watch?v=3_yD_cEKoCk',
        'https://m.youtube.com/watch?v=3_yD_cEKoCk',
        'http://m.youtube.com/watch?v=3_yD_cEKoCk',
        '//m.youtube.com/watch?v=3_yD_cEKoCk',
        'm.youtube.com/watch?v=3_yD_cEKoCk',
        'https://www.youtube.com/v/3_yD_cEKoCk?fs=1&hl=en_US',
        'http://www.youtube.com/v/3_yD_cEKoCk?fs=1&hl=en_US',
        '//www.youtube.com/v/3_yD_cEKoCk?fs=1&hl=en_US',
        'www.youtube.com/v/3_yD_cEKoCk?fs=1&hl=en_US',
        'youtube.com/v/3_yD_cEKoCk?fs=1&hl=en_US',
        'https://www.youtube.com/embed/3_yD_cEKoCk?autoplay=1',
        'https://www.youtube.com/embed/3_yD_cEKoCk',
        'http://www.youtube.com/embed/3_yD_cEKoCk',
        '//www.youtube.com/embed/3_yD_cEKoCk',
        'www.youtube.com/embed/3_yD_cEKoCk',
        'https://youtube.com/embed/3_yD_cEKoCk',
        'http://youtube.com/embed/3_yD_cEKoCk',
        '//youtube.com/embed/3_yD_cEKoCk',
        'youtube.com/embed/3_yD_cEKoCk',
        'https://youtu.be/3_yD_cEKoCk?t=120',
        'https://youtu.be/3_yD_cEKoCk',
        'http://youtu.be/3_yD_cEKoCk',
        '//youtu.be/3_yD_cEKoCk',
        'youtu.be/3_yD_cEKoCk',
      ];
      testURLs.forEach((url) => {
        expect(api.parseYoutubeId(url)).toEqual(id);
      });
    });
    it('returns null if the url is not a youtube url', () => {
      const badURL = 'https://someothersite.com/3_yD_cEKoCk';
      expect(api.parseYoutubeId(badURL)).toEqual(null);
    });
  });
  describe('processLicense', () => {
    it('returns empty string when licenseType is empty or not a valid licnese type', () => {
      expect(api.processLicense('', {})).toEqual('');
      expect(api.processLicense('LiCeNsETYpe', {})).toEqual('');
    });
    it('returns empty string when licenseType equals creative commons', () => {
      const licenseType = 'creative-commons';
      const licenseDetails = {
        attribution: true,
        noncommercial: false,
        noDerivatives: true,
        shareAlike: false,
      };
      expect(api.processLicense(licenseType, licenseDetails)).toEqual('creative-commons: ver=4.0 BY ND');
    });
    it('returns empty string when licenseType equals creative commons', () => {
      const licenseType = 'all-rights-reserved';
      const licenseDetails = {};
      expect(api.processLicense(licenseType, licenseDetails)).toEqual('all-rights-reserved');
    });
  });
});
