import expect from 'expect';
import { filter } from 'lodash';

import { MobileWebCookie } from 'mweb/common/utils/cookie';
import { SPECIAL_PATHS } from 'mweb/common/utils/redirectManager';
import { EventLocation } from 'mweb/lambda/';
import { LambdaRedirectManager } from 'mweb/lambda/lambdaRedirectManager';

describe('LambdaRedirectManager', () => {
  type SubjectParameters = {
    location?: EventLocation;
    pathParts?: ReadonlyArray<string>;
    cookie?: Readonly<MobileWebCookie>;
    redirectFromDesktop?: boolean;
    medium?: string;
    content?: string;
  };

  function subject({
    location = '',
    pathParts = [],
    cookie = {},
    redirectFromDesktop = true,
    medium,
    content,
  }: SubjectParameters): LambdaRedirectManager {
    return new LambdaRedirectManager(
      location,
      pathParts,
      cookie,
      redirectFromDesktop,
      medium,
      content,
    );
  }

  describe('shouldRedirect', () => {
    it('returns false on a valid channel', () => {
      expect(subject({ pathParts: ['monstercat'] }).redirect()).toEqual(false);
    });

    describe('exact special paths', () => {
      filter(SPECIAL_PATHS, path => path.indexOf('*') === -1).forEach(
        itReturnsTrueForSpecialPaths,
      );
    });

    describe('simple wildcards', () => {
      ['p/90', 'partner/partner', 'messages/hi', 'monstercat/chat'].forEach(
        itReturnsTrueForSpecialPaths,
      );
    });

    describe('more complicated wildcards', () => {
      [
        'messages/to/you',
        'messages/m/e/s/sa/g/e/s',
        'p/90/x',
        'partner/partnerships/partnering/partnered/partners/parteneers',
      ].forEach(itReturnsTrueForSpecialPaths);
    });

    describe('paths that extend beyond exact paths', () => {
      itReturnsFalseForNonMatchingPaths('passport/callback/extra');
    });

    describe('paths that have unexpected prefixes', () => {
      itReturnsFalseForNonMatchingPaths('extra/passport/callback');
    });

    describe('invalid chat paths', () => {
      itReturnsFalseForNonMatchingPaths('monster/cat/chat');
    });

    describe('on 404', () => {
      it('redirects back when initially redirected', () => {
        expect(
          subject({
            location: 'not_found',
            redirectFromDesktop: true,
            pathParts: ['a', 'b', 'c'],
          }).redirect(),
        ).toEqual(true);
      });

      it('does not redirect back when not initially redirected', () => {
        expect(
          subject({
            location: 'not_found',
            redirectFromDesktop: false,
            pathParts: ['a', 'b', 'c'],
          }).redirect(),
        ).toEqual(false);
      });
    });

    describe('when upselling', () => {
      it('does not redirect if an opt out has not already happened', () => {
        expect(subject({ location: 'mobile_upsell' }).redirect()).toEqual(
          false,
        );
      });

      describe('channel aliases', () => {
        it('redirects overwatchleague_allaccess', () => {
          expect(
            subject({ pathParts: ['overwatchleague_allaccess'] }).redirect(),
          ).toEqual(true);
        });
      });

      it('does not redirect if an opt out has happened, but we are not upselling', () => {
        expect(
          subject({
            location: 'directory_game',
            cookie: { upsell_opt_out: true },
          }).redirect(),
        ).toEqual(false);
      });

      it('redirects if an opt out has happened and we are upselling', () => {
        expect(
          subject({
            pathParts: ['some', 'crazy', 'upsell'],
            location: 'mobile_upsell',
            cookie: { upsell_opt_out: true },
          }).redirect(),
        ).toEqual(true);
      });
    });

    function itReturnsTrueForSpecialPaths(path: string): void {
      it(`returns true for ${path}`, () => {
        expect(subject({ pathParts: path.split('/') }).redirect()).toEqual(
          true,
        );
      });

      it(`returns true for ${path} even when not initially redirected`, () => {
        expect(
          subject({
            pathParts: path.split('/'),
            redirectFromDesktop: false,
          }).redirect(),
        ).toEqual(true);
      });
    }
    function itReturnsFalseForNonMatchingPaths(path: string): void {
      it(`returns false for ${path}`, () => {
        expect(subject({ pathParts: path.split('/') }).redirect()).toEqual(
          false,
        );
      });
    }
  });

  describe('redirectTo', () => {
    it('redirects back to the fake channel', () => {
      expect(subject({ pathParts: ['turbo'] }).redirectTo()).toEqual(
        'https://www.twitch.tv/turbo?no-mobile-redirect=true',
      );
    });

    it('redirects to a simple special path', () => {
      expect(subject({ pathParts: ['user', 'legal'] }).redirectTo()).toEqual(
        'https://www.twitch.tv/user/legal?no-mobile-redirect=true',
      );
    });

    it('redirects to a complicated special path', () => {
      expect(subject({ pathParts: ['p', '1234'] }).redirectTo()).toEqual(
        'https://www.twitch.tv/p/1234?no-mobile-redirect=true',
      );
    });

    it('redirects an upsell', () => {
      expect(
        subject({
          pathParts: ['something', 'something', 'upsell'],
          location: 'mobile_upsell',
        }).redirectTo(),
      ).toEqual(
        'https://www.twitch.tv/something/something/upsell?no-mobile-redirect=true',
      );
    });

    it('includes the content & medium', () => {
      expect(
        subject({
          pathParts: ['voxel', 'profile'],
          location: 'mobile_upsell',
          content: 'hazardous if swallowed',
          medium: 'cardboard box',
        }).redirectTo(),
      ).toEqual(
        'https://www.twitch.tv/voxel/profile?no-mobile-redirect=true&tt_content=hazardous%20if%20swallowed&tt_medium=cardboard%20box',
      );
    });

    it('includes just the medium', () => {
      expect(
        subject({
          pathParts: ['voxel', 'profile'],
          location: 'mobile_upsell',
          medium: 'cardboard box',
        }).redirectTo(),
      ).toEqual(
        'https://www.twitch.tv/voxel/profile?no-mobile-redirect=true&tt_medium=cardboard%20box',
      );
    });

    it('includes just the content', () => {
      expect(
        subject({
          pathParts: ['voxel', 'profile'],
          location: 'mobile_upsell',
          content: 'hazardous if swallowed',
        }).redirectTo(),
      ).toEqual(
        'https://www.twitch.tv/voxel/profile?no-mobile-redirect=true&tt_content=hazardous%20if%20swallowed',
      );
    });

    ['/voxel/rooms/someRoom', '/voxel/mobile'].forEach(path => {
      const pathParts = path.split('/').slice(1);
      it(`redirects to the channel from ${path}`, () => {
        expect(
          subject({
            pathParts: pathParts,
          }).redirectTo(),
        ).toEqual(`/${pathParts[0]}`);
      });

      it('maintains content and medium', () => {
        expect(
          subject({
            pathParts: pathParts,
            content: 'potassium',
            medium: 'water',
          }).redirectTo(),
        ).toEqual(`/${pathParts[0]}?tt_content=potassium&tt_medium=water`);
      });
    });

    [
      {
        path: '/overwatchleague_allaccess',
        to: 'overwatchleague',
      },
    ].forEach(({ path, to }) => {
      const pathParts = path.split('/').slice(1);

      it(`redirects to ${to} from ${path}`, () => {
        expect(
          subject({
            pathParts: pathParts,
          }).redirectTo(),
        ).toEqual(`/${to}`);
      });

      it('maintains content and medium', () => {
        expect(
          subject({
            pathParts: pathParts,
            content: 'potassium',
            medium: 'water',
          }).redirectTo(),
        ).toEqual(`/${to}?tt_content=potassium&tt_medium=water`);
      });
    });
  });
});
