import { getUrl } from './getters';
import * as Schema from './schema';
import { processSpeakableModules } from './schema';

const parseCheckSchema = (schema) => {
  const json = JSON.parse(schema);
  expect(Object.prototype.toString.call(schema)).toEqual('[object String]');
  expect(Object.prototype.toString.call(json)).toEqual('[object Object]');
  expect(json['@context']).toEqual('https://schema.org');
  return json;
};
const pageImage = 'pageImage/pageImage.jpg';

describe('Schema Helper', () => {
  test('breadcrumbsSchema()', () => {
    const output = Schema.breadcrumbsSchema({
      path: 'some_path',
      title: 'Some Title Review',
      breadcrumbs: [
        { path: 'breadcrumb_a', label: 'Breadcrumb A' },
        { path: 'breadcrumb_b', label: 'Breadcrumb B' },
      ],
      siteSchema: {
        site_name: 'Site Name',
      },
    });

    const json = parseCheckSchema(output);
    expect(json['@type']).toEqual('BreadcrumbList');

    expect(Object.prototype.toString.call(json.itemListElement)).toEqual('[object Array]');
    expect(json.itemListElement).toHaveLength(4);
    json.itemListElement.forEach((element, i) => {
      expect(Object.prototype.toString.call(element)).toEqual('[object Object]');
      expect(Object.prototype.toString.call(element.item)).toEqual('[object Object]');
      expect(element['@type']).toEqual('ListItem');
      expect(element.position).toEqual(i + 1);
      switch (i) {
        case 1:
          expect(element.name).toEqual('Breadcrumb A');
          expect(element.item['@type']).toEqual('Thing');
          expect(element.item['@id']).toEqual(getUrl('breadcrumb_a'));
          expect(element.item.url).toEqual(getUrl('breadcrumb_a'));
          break;
        case 2:
          expect(element.name).toEqual('Breadcrumb B');
          expect(element.item['@type']).toEqual('Thing');
          expect(element.item['@id']).toEqual(getUrl('breadcrumb_b'));
          expect(element.item.url).toEqual(getUrl('breadcrumb_b'));
          break;
        case 3:
          expect(element.name).toEqual('Some Title Review');
          expect(element.item['@type']).toEqual('Thing');
          expect(element.item['@id']).toEqual(getUrl('some_path'));
          expect(element.item.url).toEqual(getUrl('some_path'));
          break;
        default:
          expect(element.name).toEqual('Site Name');
          expect(element.item['@type']).toEqual('Thing');
          expect(element.item['@id']).toEqual(getUrl('/'));
          expect(element.item.url).toEqual(getUrl('/'));
          break;
      }
    });
  });

  describe('processSpeakableModules()', () => {
    test('should return an empty array if no speakable modules are provided', () => {
      const modules = [
        { name: 'other' },
        { name: 'faq', speakable_enabled: '0' },
        { name: 'content', speakable_enabled: '0' },
      ];

      const result = processSpeakableModules(modules);

      expect(result).toEqual([]);
    });

    test('should return an array of speakable modules', () => {
      const modules = [
        { name: 'other' },
        {
          name: 'content',
          speakable_enabled: '1',
          path: '/content-module',
          title: 'SpeakableTitle',
          tag: 'h2',
        },
      ];

      const result = processSpeakableModules(modules);

      const expectedResults = [
        JSON.stringify({
          '@context': 'https://schema.org',
          '@type': 'WebPage',
          name: 'SpeakableTitle',
          speakable: {
            '@type': 'SpeakableSpecification',
            cssSelector: ['#module_undefined h2', '#module_undefined p'], // Updated expected value
          },
          url: getUrl('/content-module'),
        }),
      ];

      expect(result).toEqual(expectedResults);
    });
  });

  test('webPageSchema()', () => {
    const output = Schema.webPageSchema(
      {
        path: 'web_page',
        meta_title: 'Meta Title',
        meta_description: 'Meta Description',
        language: 'site_lang',
        created_at: '01/01/01',
        updated_at: '02/02/02',
        seo_keywords: ['keyword_a', 'keyword_b'],
        authors: [
          {
            profile_page_path: 'author_a/author_page',
            email_address: 'email_a@email.com',
            name: 'Author_A',
          },
          {
            profile_page_path: 'author_b/author_page',
            email_address: 'email_b@email.com',
            name: 'Author_B',
          },
        ],
        siteSchema: {
          site_name: 'Site Name',
          alias_site_name: 'Alias Site Name',
          organization_founding_date: '01/02/03',
          linkedin: 'author_linkedin',
          spotify: 'author_spotify',
          wikipedia: 'author_wikipedia',
        },
      },
      pageImage
    );

    const json = parseCheckSchema(output);
    expect(json['@type']).toEqual('WebPage');
    expect(json['@id']).toEqual(`${getUrl('web_page')}#webpage`);
    expect(json.url).toEqual(getUrl('web_page'));
    expect(json.name).toEqual('Meta Title');
    expect(json.description).toEqual('Meta Description');
    expect(json.inLanguage).toEqual('site_lang');
    expect(json.datePublished).toEqual('01/01/01');
    expect(json.dateModified).toEqual('02/02/02');

    expect(Object.prototype.toString.call(json.isPartOf)).toEqual('[object Object]');
    expect(json.isPartOf['@type']).toEqual('WebSite');
    expect(json.isPartOf['@id']).toEqual(`${process.env.GATSBY_SITE_URL}#website`);
    expect(json.isPartOf.url).toEqual(`${process.env.GATSBY_SITE_URL}`);
    expect(json.isPartOf.inLanguage).toEqual('site_lang');

    expect(Object.prototype.toString.call(json.primaryImageOfPage)).toEqual('[object Object]');
    expect(json.primaryImageOfPage['@type']).toEqual('ImageObject');
    expect(json.primaryImageOfPage['@id']).toEqual(`${getUrl('web_page')}#primaryimage`);
    expect(json.primaryImageOfPage.url).toEqual(pageImage);
    expect(json.primaryImageOfPage.inLanguage).toEqual('site_lang');

    expect(Object.prototype.toString.call(json.author)).toEqual('[object Object]');
    expect(json.author['@type']).toEqual('Organization');
    expect(json.author.name).toEqual('Site Name');
    expect(json.author.alternateName).toEqual('Alias Site Name');
    expect(json.author.foundingDate).toEqual('01/02/03');
    expect(json.author.publishingPrinciples).toEqual(undefined);

    expect(Object.prototype.toString.call(json.author.logo)).toEqual('[object Object]');
    expect(json.author.logo['@type']).toEqual('ImageObject');
    expect(json.author.logo.url).toEqual(pageImage);

    expect(Object.prototype.toString.call(json.author.contactPoint)).toEqual('[object Array]');
    expect(json.author.contactPoint).toHaveLength(2);
    expect(Object.prototype.toString.call(json.author.contactPoint[0])).toEqual('[object Object]');
    expect(Object.prototype.toString.call(json.author.contactPoint[1])).toEqual('[object Object]');
    expect(json.author.contactPoint[0]['@type']).toEqual('ContactPoint');
    expect(json.author.contactPoint[1]['@type']).toEqual('ContactPoint');
    expect(json.author.contactPoint[0].contactType).toEqual('Author');
    expect(json.author.contactPoint[1].contactType).toEqual('Author');
    expect(json.author.contactPoint[0].url).toEqual(`${getUrl('/')}author_a/author_page`);
    expect(json.author.contactPoint[1].url).toEqual(`${getUrl('/')}author_b/author_page`);
    expect(json.author.contactPoint[0].name).toEqual('Author_A');
    expect(json.author.contactPoint[1].name).toEqual('Author_B');
    expect(json.author.contactPoint[0].email).toEqual('email_a@email.com');
    expect(json.author.contactPoint[1].email).toEqual('email_b@email.com');

    expect(Object.prototype.toString.call(json.author.sameAs)).toEqual('[object Array]');
    expect(json.author.sameAs).toHaveLength(3);
    expect(json.author.sameAs[0]).toEqual('author_linkedin');
    expect(json.author.sameAs[1]).toEqual('author_spotify');
    expect(json.author.sameAs[2]).toEqual('author_wikipedia');
    expect(json.author.knowsAbout).toEqual(undefined);

    expect(Object.prototype.toString.call(json.keywords)).toEqual('[object Array]');
    expect(json.keywords).toHaveLength(2);
    expect(json.keywords[0]).toEqual('keyword_a');
    expect(json.keywords[1]).toEqual('keyword_b');
  });

  test('organizationSchema()', () => {
    const output = Schema.organizationSchema(
      {
        siteSchema: {
          site_name: 'Site Name',
          linkedin: 'organization_linkedin',
          spotify: 'organization_spotify',
          wikipedia: 'organization_wikipedia',
        },
      },
      pageImage
    );

    const json = parseCheckSchema(output);
    expect(json['@type']).toEqual('Organization');
    expect(json.url).toEqual(process.env.GATSBY_SITE_URL);
    expect(json.name).toEqual('Site Name');
    expect(json.logo).toEqual(pageImage);

    expect(Object.prototype.toString.call(json.sameAs)).toEqual('[object Array]');
    expect(json.sameAs).toHaveLength(3);
    expect(json.sameAs[0]).toEqual('organization_linkedin');
    expect(json.sameAs[1]).toEqual('organization_spotify');
    expect(json.sameAs[2]).toEqual('organization_wikipedia');
  });

  test('moduleSchemas(null)', () => {
    const output = Schema.moduleSchemas([{ name: 'other' }], '/');

    expect(output).toHaveLength(0);
    expect(output[0]).toEqual(undefined);
  });

  test('moduleSchemas(faq)', () => {
    const output = Schema.moduleSchemas(
      [
        {
          name: 'faq',
          render_schema: '1',
          items: [
            { answer: 'Answer A', question: 'Question A' },
            { answer: 'Answer B', question: 'Question B' },
            { answer: 'Answer C', question: 'Question C' },
          ],
        },
      ],
      '/'
    )[0];

    const json = parseCheckSchema(output);
    expect(json['@type']).toEqual('FAQPage');

    expect(Object.prototype.toString.call(json.mainEntity)).toEqual('[object Array]');
    expect(json.mainEntity).toHaveLength(3);
    expect(Object.prototype.toString.call(json.mainEntity[0])).toEqual('[object Object]');
    expect(Object.prototype.toString.call(json.mainEntity[1])).toEqual('[object Object]');
    expect(Object.prototype.toString.call(json.mainEntity[2])).toEqual('[object Object]');
    expect(json.mainEntity[0]['@type']).toEqual('Question');
    expect(json.mainEntity[1]['@type']).toEqual('Question');
    expect(json.mainEntity[2]['@type']).toEqual('Question');
    expect(json.mainEntity[0].name).toEqual('Question A');
    expect(json.mainEntity[1].name).toEqual('Question B');
    expect(json.mainEntity[2].name).toEqual('Question C');

    expect(Object.prototype.toString.call(json.mainEntity[0].acceptedAnswer)).toEqual(
      '[object Object]'
    );
    expect(Object.prototype.toString.call(json.mainEntity[1].acceptedAnswer)).toEqual(
      '[object Object]'
    );
    expect(Object.prototype.toString.call(json.mainEntity[2].acceptedAnswer)).toEqual(
      '[object Object]'
    );
    expect(json.mainEntity[0].acceptedAnswer['@type']).toEqual('Answer');
    expect(json.mainEntity[1].acceptedAnswer['@type']).toEqual('Answer');
    expect(json.mainEntity[2].acceptedAnswer['@type']).toEqual('Answer');
    expect(json.mainEntity[0].acceptedAnswer.text).toEqual('Answer A');
    expect(json.mainEntity[1].acceptedAnswer.text).toEqual('Answer B');
    expect(json.mainEntity[2].acceptedAnswer.text).toEqual('Answer C');
  });

  test('templateSchemas(null)', () => {
    const output = Schema.templateSchemas({
      relation_type: 'other',
      path: 'other_page',
    });

    expect(output).toEqual(null);
  });

  test('templateSchemas(article)', () => {
    const output = Schema.templateSchemas({
      relation_type: 'article',
      path: 'article_page',
      title: 'Article Title',
      created_at: '01/01/01',
      updated_at: '02/02/02',
      featured_image_object: {
        url: 'featured_image/image.jpg',
      },
      author: {
        name: 'Very long name for the 100 characters Article Author test - ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ',
        profile_page_path: 'article_author/author_page',
      },
      siteInfo: {
        site_logo: 'logo.jpg',
      },
    });

    const json = parseCheckSchema(output);
    expect(json['@type']).toEqual('Article');
    expect(json.url).toEqual(getUrl('article_page'));
    expect(json.headline).toEqual('Article Title');
    expect(json.datePublished).toEqual('01/01/01');
    expect(json.dateModified).toEqual('02/02/02');

    expect(Object.prototype.toString.call(json.mainEntityOfPage)).toEqual('[object Object]');
    expect(json.mainEntityOfPage['@type']).toEqual('WebPage');
    expect(json.mainEntityOfPage.url).toEqual(process.env.GATSBY_SITE_URL);

    expect(Object.prototype.toString.call(json.author)).toEqual('[object Object]');
    expect(json.author['@type']).toEqual('Person');
    expect(json.author.name).toHaveLength(100);
    expect(json.author.name).toEqual(
      'Very long name for the 100 characters Article Author test - ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMN'
    );
    expect(json.author.url).toEqual(`${getUrl('/')}article_author/author_page`);

    expect(Object.prototype.toString.call(json.publisher)).toEqual('[object Object]');
    expect(json.publisher['@type']).toEqual('Organization');
    expect(json.publisher.name).toBeTruthy();
    expect(json.publisher.logo).toEqual(`${process.env.IMAGE_CDN_URL}/logo.jpg`);
  });

  test('templateSchemas(operator)', () => {
    const output = Schema.templateSchemas({
      relation_type: 'operator',
      path: 'operator_page',
      title: 'Operator Title',
      relation: {
        name: 'Operator Relation Name',
        rating: 3.5,
        logo_url_object: {
          url: 'logo_url/image.jpg',
        },
      },
      featured_image_object: {
        url: 'featured_image/image.jpg',
      },
      author: {
        name: 'Very long name for the 100 characters Article Author test - ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ',
        profile_page_path: 'operator_review_author/author_page',
      },
      siteInfo: {
        site_logo: 'logo.jpg',
      },
      extra_fields: {
        operator_summary: 'Operator Review Summary',
      },
    });

    const json = parseCheckSchema(output);
    expect(json['@type']).toEqual('Review');
    expect(json.name).toEqual('Operator Relation Name');
    expect(json.reviewBody).toEqual('Operator Review Summary');

    expect(Object.prototype.toString.call(json.itemReviewed)).toEqual('[object Object]');
    expect(json.itemReviewed['@type']).toEqual('Organization');
    expect(json.itemReviewed.url).toEqual(getUrl('operator_page'));
    expect(json.itemReviewed.name).toEqual('Operator Title');
    expect(json.itemReviewed.image).toEqual('logo_url/image.jpg');

    expect(Object.prototype.toString.call(json.reviewRating)).toEqual('[object Object]');
    expect(json.reviewRating['@type']).toEqual('Rating');
    expect(json.reviewRating.bestRating).toEqual('5');
    expect(json.reviewRating.worstRating).toEqual('0');
    expect(json.reviewRating.ratingValue).toEqual('3.5');

    expect(Object.prototype.toString.call(json.author)).toEqual('[object Object]');
    expect(json.author['@type']).toEqual('Person');
    expect(json.author.name).toHaveLength(100);
    expect(json.author.name).toEqual(
      'Very long name for the 100 characters Article Author test - ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMN'
    );
    expect(json.author.url).toEqual(`${getUrl('/')}operator_review_author/author_page`);

    expect(Object.prototype.toString.call(json.publisher)).toEqual('[object Object]');
    expect(json.publisher['@type']).toEqual('Organization');
    expect(json.publisher.name).toBeTruthy();
    expect(json.publisher.logo).toEqual(`${process.env.IMAGE_CDN_URL}/logo.jpg`);
  });

  test('templateSchemas(game)', () => {
    const output = Schema.templateSchemas({
      relation_type: 'game',
      path: 'game_page',
      relation: {
        name: 'Game Relation Name',
        first_rating: 3.5,
        third_rating: 1.5,
      },
      featured_image_object: {
        url: 'featured_image/image.jpg',
      },
      author: {
        name: 'Very long name for the 100 characters Article Author test - ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ',
        profile_page_path: 'operator_review_author/author_page',
      },
      siteInfo: {
        site_logo: 'logo.jpg',
      },
      extra_fields: {
        game_summary: 'Game Review Summary',
      },
    });

    const json = parseCheckSchema(output);
    expect(json['@type']).toEqual('VideoGame');
    expect(json.url).toEqual(getUrl('game_page'));
    expect(json.name).toEqual('Game Relation Name');
    expect(json.description).toEqual('Game Review Summary');
    expect(json.operatingSystem).toEqual('Multi-platform');

    expect(Object.prototype.toString.call(json.applicationCategory)).toEqual('[object Array]');
    expect(json.applicationCategory).toHaveLength(1);
    expect(json.applicationCategory[0]).toEqual('Game');

    expect(Object.prototype.toString.call(json.author)).toEqual('[object Object]');
    expect(json.author['@type']).toEqual('Organization');
    expect(json.author.name).toEqual(process.env.GATSBY_SITE_NAME);
    expect(json.author.url).toEqual(process.env.GATSBY_SITE_URL);

    expect(Object.prototype.toString.call(json.review)).toEqual('[object Object]');
    expect(json.review['@type']).toEqual('Review');
    expect(json.review.name).toEqual('Game Relation Name');

    expect(Object.prototype.toString.call(json.review.reviewRating)).toEqual('[object Object]');
    expect(json.review.reviewRating['@type']).toEqual('Rating');
    expect(json.review.reviewRating.bestRating).toEqual('10');
    expect(json.review.reviewRating.worstRating).toEqual('0');
    expect(json.review.reviewRating.ratingValue).toEqual('2');

    expect(Object.prototype.toString.call(json.review.author)).toEqual('[object Object]');
    expect(json.review.author['@type']).toEqual('Person');
    expect(json.review.author.name).toHaveLength(100);
    expect(json.review.author.name).toEqual(
      'Very long name for the 100 characters Article Author test - ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMN'
    );
    expect(json.review.author.url).toEqual(`${getUrl('/')}operator_review_author/author_page`);

    expect(Object.prototype.toString.call(json.review.publisher)).toEqual('[object Object]');
    expect(json.review.publisher['@type']).toEqual('Organization');
    expect(json.review.publisher.name).toBeTruthy();
    expect(json.review.publisher.logo).toEqual(`${process.env.IMAGE_CDN_URL}/logo.jpg`);
  });

  test('schemaGenerator()', () => {
    const output = Schema.schemaGenerator({
      seo_json_schema: 'SEO JSON Schema',
      breadcrumbs: [{ path: '/test' }],
      relation_type: 'article',
      path: '/test-path',
      sections: { main: { modules: [{ name: 'faq', render_schema: '1' }] } },
    });

    expect(Object.prototype.toString.call(output)).toEqual('[object Array]');

    expect(Object.prototype.toString.call(output[0])).toEqual('[object String]');
    expect(output[0]).toEqual('SEO JSON Schema');

    const jsonBreadcrumbs = parseCheckSchema(output[1]);
    expect(jsonBreadcrumbs['@type']).toEqual('BreadcrumbList');

    const jsonWebpage = parseCheckSchema(output[2]);
    expect(jsonWebpage['@type']).toEqual('WebPage');
    expect(jsonWebpage.keywords).toEqual(undefined);
    expect(jsonWebpage.knowsAbout).toEqual(undefined);

    const jsonOrganization = parseCheckSchema(output[3]);
    expect(jsonOrganization['@type']).toEqual('Organization');

    const jsonArticle = parseCheckSchema(output[4]);
    expect(jsonArticle['@type']).toEqual('Article');

    const jsonFaq = parseCheckSchema(output[5]);
    expect(jsonFaq['@type']).toEqual('FAQPage');
  });

  test('schemaGenerator() seo_json_schemas', () => {
    const output = Schema.schemaGenerator({
      seo_json_schemas: ['SEO JSON Schema A', 'SEO JSON Schema B'],
      breadcrumbs: [{ path: '/test' }],
      relation_type: 'article',
      path: '/test-path',
      sections: { main: { modules: [{ name: 'faq' }] } },
    });

    expect(Object.prototype.toString.call(output)).toEqual('[object Array]');

    expect(Object.prototype.toString.call(output[0])).toEqual('[object String]');
    expect(output[0]).toEqual('SEO JSON Schema A');
    expect(output[1]).toEqual('SEO JSON Schema B');

    const jsonBreadcrumbs = parseCheckSchema(output[2]);
    expect(jsonBreadcrumbs['@type']).toEqual('BreadcrumbList');
  });
});
