const precompile = require('../precompile');

describe('legacy precompile', () => {
  describe('partials', () => {
    test('should resolve partials', () => {
      const newLocal = ` {% include 'some-include' %} `;
      const result = precompile(newLocal, {
        'some-include': `{{ 'my_custom_setting' | setting }} `
      });
      expect(result).toContain('function template_en(');
      expect(result).toContain('"en": template_en');
    });

    test('should not fail if missing', () => {
      const newLocal = `{% include "missing.html" ignore missing %} `;
      const result = precompile(newLocal, {});

      expect(result).toContain('function template_en(');
      expect(result).toContain('"en": template_en');
    });

    test('should resolve partials inside partials', () => {
      const newLocal = ` {% include 'partial-a' %} `;
      const result = precompile(newLocal, {
        'partial-a': `{% include 'partial-b' %}`,
        'partial-b': `yum partial b`
      });
      expect(result).toContain('function template_en(');
      expect(result).toContain('"en": template_en');
      expect(result).toContain('yum partial b');
    });

    test('should resolve partials with extension', () => {
      const newLocal = ` {% include 'some-include' %} `;
      const result = precompile(newLocal, {
        'some-include.liquid': `{{ 'my_custom_setting' | setting }} `
      });
      expect(result).toContain('function template_en(');
      expect(result).toContain('"en": template_en');
    });

    test('should fail if partial not found', () => {
      const newLocal = ` {% include 'some-include' %} `;
      expect(() => precompile(newLocal)).toThrow('Template include not found some-include');
    });
  });

  describe('locales', () => {
    test('should compile template function name in snake case', () => {
      const newLocal = ` {{ 'my_custom_setting' | setting }} `;
      const result = precompile(newLocal, {}, [
        {
          locale: 'en-US'
        }
      ]);
      expect(result).toContain('function template_en_us(');
      expect(result).toContain('"en-US": template_en_us');
    });

    test('should compile default en template', () => {
      const newLocal = ` {{ 'my_custom_setting' | setting }} `;
      const result = precompile(newLocal);
      expect(result).toContain('function template_en(');
      expect(result).toContain('"en": template_en');
    });

    test('should compile default fr template', () => {
      const newLocal = ` {{ 'my_custom_setting' | setting }} `;
      const result = precompile(newLocal, {}, [
        {
          locale: 'fr'
        }
      ]);
      expect(result).toContain('function template_fr(');
      expect(result).toContain('"fr": template_fr');
    });

    test('should allow objects in locales', () => {
      const newLocal = ` {% set test = 'an_object' | t %} `;
      const result = precompile(newLocal, {}, [
        {
          locale: 'en',
          translations: {
            an_object: {
              foo: 'bar',
              'white spaced key': 'should work'
            }
          }
        }
      ]);
      expect(result).toContain('"foo": "bar"');
      expect(result).toContain('"white spaced key": "should work"');
    });

    test('should replace translation text optimized', () => {
      const newLocal = ` {{ 'my_custom_text' | t }} `;
      const result = precompile(newLocal, {}, [
        {
          locale: 'it',
          translations: { my_custom_text: 'Fancy text' }
        }
      ]);
      expect(result).toContain('function template_it(');
      expect(result).toContain('"it": template_it');
      expect(result).toContain('html` Fancy text `');
    });

    test('should replace translation text with an empty string, if not key is not found in locale setting', () => {
      const newLocal = ` {{ 'my_custom_text' | t }} `;
      const result = precompile(newLocal, {}, [
        {
          locale: 'it',
          translations: {}
        }
      ]);
      expect(result).toContain('function template_it(');
      expect(result).toContain('"it": template_it');
      expect(result).toContain('return html`  `;');
    });

    test('should replace some_setting', () => {
      const newLocal = `
    {% if some_value == ('some_setting' | setting) %}
       yay
    {% endif %}
    `;

      const result = precompile(newLocal, {}, [], {
        some_setting: 'foo bar'
      });

      expect(result).toContain('some_value == ("foo bar") ?');
    });

    test('should default setting if not present', () => {
      const newLocal = `
    {% if some_value == ('some_setting' | setting('yes the default')) %}
       yay
    {% endif %}
    `;

      const result = precompile(newLocal);

      expect(result).toContain('some_value == ("yes the default") ?');
    });
    test('should setting undefined if no default', () => {
      const newLocal = `
    {% if some_value == ('some_setting' | setting ) %}
       yay
    {% endif %}
    `;

      const result = precompile(newLocal);

      expect(result).toContain('some_value == (undefined) ?');
    });

    test('should allow html', () => {
      const newLocal = ` {{ 'my_custom_text' | t }} `;
      const result = precompile(newLocal, {}, [
        {
          locale: 'it',
          translations: { my_custom_text: '<strong>This is string</strong>' }
        }
      ]);
      expect(result).toContain('function template_it(');
      expect(result).toContain('"it": template_it');
      expect(result).toContain('html` <strong>This is string</strong> `');
    });

    test('should allow html in translations with value expressions', () => {
      const newLocal = ` {{ 'my_custom_text' | t }} `;
      const result = precompile(newLocal, {}, [
        {
          locale: 'it',
          translations: { my_custom_text: '<strong>{{"This is string"|foo}}</strong>' }
        }
      ]);
      expect(result).toContain('function template_it(');
      expect(result).toContain('"it": template_it');
      expect(result).toContain('html` ${html`<strong>${_F.foo("This is string")}</strong>`} `');
    });

    test('should compile currency filter with locale rules', () => {
      const newLocal = ` {{ 123456.789 | currency }} `;
      const result = precompile(newLocal, {}, [
        {
          locale: 'it',
          translations: {
            currency_code: 'USD',
            currency_display: 'narrowSymbol'
          }
        }
      ]);

      expect(result).toContain('function template_it(');
      expect(result).toContain('"it": template_it');
      expect(result).toContain('${_F.currency(123456.789, "USD", "narrowSymbol")}');
    });

    test('should compile currency filter with default values if no currency settings are set', () => {
      const newLocal = ` {{ 123456.789 | currency }} `;
      const result = precompile(newLocal, {}, [
        {
          locale: 'it',
          translations: {}
        }
      ]);

      expect(result).toContain('function template_it(');
      expect(result).toContain('"it": template_it');
      expect(result).toContain('${_F.currency(123456.789, undefined, undefined)}');
    });

    test('should compile currency filter with default values if empty currency settings are set', () => {
      const newLocal = ` {{ 123456.789 | currency }} `;
      const result = precompile(newLocal, {}, [
        {
          locale: 'it',
          translations: {
            currency_code: '',
            currency_display: ''
          }
        }
      ]);

      expect(result).toContain('function template_it(');
      expect(result).toContain('"it": template_it');
      expect(result).toContain('${_F.currency(123456.789, undefined, undefined)}');
    });

    test('should compile currency filter with default values if invalid currency settings are set', () => {
      const newLocal = ` {{ 123456.789 | currency }} `;
      const result = precompile(newLocal, {}, [
        {
          locale: 'it',
          translations: {
            currency_code: 'NOTREAL',
            currency_display: 'NOTREAL'
          }
        }
      ]);

      expect(result).toContain('function template_it(');
      expect(result).toContain('"it": template_it');
      expect(result).toContain('${_F.currency(123456.789, undefined, undefined)}');
    });

    test('should compile currency filter with explicit currency code and locale currency_display', () => {
      const newLocal = ` {{ 123456.789 | currency('EUR') }} `;
      const result = precompile(newLocal, {}, [
        {
          locale: 'it',
          translations: {
            currency_display: 'narrowSymbol'
          }
        }
      ]);

      expect(result).toContain('function template_it(');
      expect(result).toContain('"it": template_it');
      expect(result).toContain('${_F.currency(123456.789, "EUR", "narrowSymbol")}');
    });

    test('should compile currency filter with explicit currency code and default currency_display if not set', () => {
      const newLocal = ` {{ 123456.789 | currency('EUR') }} `;
      const result = precompile(newLocal, {}, [
        {
          locale: 'it',
          translations: {}
        }
      ]);

      expect(result).toContain('function template_it(');
      expect(result).toContain('"it": template_it');
      expect(result).toContain('${_F.currency(123456.789, "EUR", undefined)}');
    });

    test('should compile currency filter with explicit currency code and default currency_display if invalid', () => {
      const newLocal = ` {{ 123456.789 | currency('EUR') }} `;
      const result = precompile(newLocal, {}, [
        {
          locale: 'it',
          translations: {
            currency_display: 'NOTREAL'
          }
        }
      ]);

      expect(result).toContain('function template_it(');
      expect(result).toContain('"it": template_it');
      expect(result).toContain('${_F.currency(123456.789, "EUR", undefined)}');
    });
  });

  test('demo', () => {
    const result = precompile(
      `{% if some_value == ('some_setting' | setting) %}
      {% include 'some-include' %}
    {% endif %}`,
      {
        'some-include.liquid': `{{ 'my_custom_text' | t }}`
      },
      [
        {
          locale: 'en',
          translations: { my_custom_text: 'Hello world' }
        },
        {
          locale: 'es',
          translations: { my_custom_text: 'Hola mundo' }
        }
      ],
      {
        some_setting: 'yes'
      }
    );

    expect(result).toContain('Hola mundo');
    expect(result).toContain('Hello world');
    expect(result).toContain('"en": template_en');
    expect(result).toContain('"es": template_es');
  });

  // "change_subscription_frequency_success": "Success! This item will now ship every {{frequency.every}} . Your next shipment date has not changed.",
  test('should accept named params', () => {
    const newLocal = ` {{'frequency_period' | t(period=2)}} `;
    const result = precompile(newLocal, {}, [
      {
        locale: 'en',
        translations: {
          frequency_period: 'the {{ period }} periodtext'
        }
      }
    ]);
    expect(result).toContain('(period => html`the ${period} periodtext`)(2)');
  });

  describe('setting optimization', () => {
    test('should replace false literals with expression alternae', () => {
      const newLocal = ` {% if ('some_setting_always_false' | setting) %} always true {% endif %}
    `;

      [0, undefined, '', false, -0, null, NaN, ``, ''].forEach(some_setting_always_false => {
        const result = precompile(newLocal, {}, [], {
          some_setting_always_false
        });
        expect(result).not.toContain('always true');
      });
    });
    test('should replace truthy literals with expression subsequent', () => {
      const newLocal = ` {% if ('some_setting_always_true' | setting) %} always true {% else %} always false{% endif %}
    `;

      [true, 1, {}, [], '0'].forEach(some_setting_always_true => {
        const result = precompile(newLocal, {}, [], {
          some_setting_always_true
        });
        expect(result).not.toContain('always false');
      });
    });
    test('should not touch other expressions', () => {
      const newLocal = ` {% if (myval==='some_setting_always_true' | setting) %} always true {% else %} always false{% endif %}
    `;

      [0, undefined, '', false, -0, null, NaN, ``, '', true, 1, {}, [], '0'].forEach(some_setting_always_true => {
        const result = precompile(newLocal, {}, [], {
          some_setting_always_true
        });
        expect(result).toContain('always false');
        expect(result).toContain('always true');
      });
    });
  });
});

// CODE EDITOR VERSION OF PRECOMPILE USAGE
describe('code editor precompile', () => {
  describe('files api', () => {
    test('should precompile', async () => {
      const result = await precompile([
        {
          name: '/views/main.liquid',
          content: "{% include 'some-include' %}"
        },
        {
          name: '/views/some-include.liquid',
          content: 'This is the content'
        }
      ]);

      expect(result).toContain('function template_en(');
      expect(result).toContain('"en": template_en');
    });

    test('should handle nested folders', async () => {
      const result = await precompile([
        {
          name: '/views/main.liquid',
          content: "{% include 'nested/some-include' %}"
        },
        {
          name: '/views/nested/some-include.liquid',
          content: "This is the content {% include 'other' %}"
        },
        {
          name: '/views/nested/other.liquid',
          content:
            'This is a file that should not be rendered, since template includes start from the root of the view directory'
        },
        {
          name: '/views/other.liquid',
          content: 'This is other content in the main folder'
        }
      ]);

      expect(result).toContain('This is the content');
      expect(result).toContain('This is other content in the main folder');
    });

    test('should resolve includes starting from the views directory', async () => {
      const result = await precompile([
        {
          name: '/views/main.liquid',
          content: "{% include 'views/other' %}"
        },
        {
          name: '/views/other.liquid',
          content: "I'm in the root level views folder"
        },
        {
          name: '/views/views/other.liquid',
          content: "I'm in a nested views folder"
        }
      ]);

      expect(result).toContain("I'm in a nested views folder");
    });

    test('does not allow including templates outside the views folder', async () => {
      await expect(() =>
        precompile([
          {
            name: '/views/main.liquid',
            content: "{% include 'other' %}"
          },
          {
            name: 'other.liquid',
            content: 'not in views'
          }
        ])
      ).rejects.toThrow();
    });

    test('should compile template function name in snake case', async () => {
      const result = await precompile([
        {
          name: '/views/main.liquid',
          content: "{% include 'some-include' %}"
        },
        {
          name: '/views/some-include.liquid',
          content: 'This is {{ "some-key" | t }}'
        },
        {
          name: '/locales/fr-CA.json',
          content: '{"some-key":"the translation"}'
        }
      ]);
      expect(result).toContain('function template_fr_ca(');
      expect(result).toContain('"fr-CA": template_fr_ca');
      expect(result).toContain('return html`This is the translation`;');
    });
    test('should compile template and css', async () => {
      const result = await precompile([
        {
          name: '/views/main.liquid',
          content: "{% include 'some-include' %}"
        },
        {
          name: '/views/some-include.liquid',
          content: 'This is {{ "some-key" | t }}'
        },
        {
          name: '/locales/fr-CA.json',
          content: '{"some-key":"the translation"}'
        },
        {
          name: '/styles/main.less',
          content: `@import "../file-one.less";`
        },
        {
          name: '/file-one.less',
          content: `@import "./styles/content.less";`
        },
        {
          name: '/styles/content.less',
          content: `body {background: darken(#09F, 20%);}`
        }
      ]);
      expect(result).toContain('function template_fr_ca(');
      expect(result).toContain('"fr-CA": template_fr_ca');
      expect(result).toContain('return html`This is the translation`;');
      expect(result).toContain('css.appendChild(document.createTextNode("body {\\n  background: #005c99;\\n}\\n"));');
    });

    test('should return an object if `objectReturn` flag is set', async () => {
      const result = await precompile(
        [
          {
            name: '/views/main.liquid',
            content: '<h1>Hello World</h1>'
          },
          {
            name: '/styles/main.less',
            content: `body { color: red; }`
          }
        ],
        {},
        'en',
        { objectReturn: true }
      );
      expect(result).toEqual({
        template: expect.stringContaining('<h1>Hello World</h1>'),
        css: expect.stringContaining('color: red;')
      });
      // template function still includes the CSS
      expect(result.template).toContain('color: red');
    });
  });

  describe('files api settings', () => {
    test('should compile template and settings from SSPC', async () => {
      const result = await precompile(
        [
          {
            name: '/views/main.liquid',
            content: "{% include 'some-include' %}"
          },
          {
            name: '/views/some-include.liquid',
            content: 'This is {{ "some-key" | t }} {{ "some_setting" | setting }}'
          },
          {
            name: '/locales/fr-CA.json',
            content: '{"some-key":"the translation"}'
          }
        ],
        {
          some_setting: 'showing setting'
        }
      );
      expect(result).toContain('return html`This is the translation showing setting`;');
    });

    test('should compile template and settings from SSPC and theme-settings.json', async () => {
      const result = await precompile(
        [
          {
            name: '/views/main.liquid',
            content: "{% include 'some-include' %}"
          },
          {
            name: '/views/some-include.liquid',
            content: 'This is {{ "some-key" | t }} {{ "some_setting" | setting }} {{ "show-my-fav-key" | setting }}'
          },
          {
            name: '/locales/fr-CA.json',
            content: '{"some-key":"the translation"}'
          },
          {
            name: '/controls/theme-settings.json',
            content: '{"show-my-fav-key":"basic knobs setting"}'
          }
        ],
        {
          some_setting: 'showing setting'
        }
      );
      expect(result).toContain('return html`This is the translation showing setting basic knobs setting`;');
    });

    test('should prioritize theme-settings.json settings', async () => {
      const result = await precompile(
        [
          {
            name: '/views/main.liquid',
            content: "{% include 'some-include' %}"
          },
          {
            name: '/views/some-include.liquid',
            content: 'This is {{ "some-key" | t }} {{ "duplicate" | setting }}'
          },
          {
            name: '/locales/fr-CA.json',
            content: '{"some-key":"the translation"}'
          },
          {
            name: '/controls/theme-settings.json',
            content: '{"duplicate":"basic knobs setting"}'
          }
        ],
        {
          duplicate: 'showing setting'
        }
      );
      expect(result).toContain('return html`This is the translation basic knobs setting`;');
    });
    test('should still show SSPC settings if blank theme-settings', async () => {
      const result = await precompile(
        [
          {
            name: '/views/main.liquid',
            content: "{% include 'some-include' %}"
          },
          {
            name: '/views/some-include.liquid',
            content: 'This is {{ "some-key" | t }} {{ "some_setting" | setting }}'
          },
          {
            name: '/locales/fr-CA.json',
            content: '{"some-key":"the translation"}'
          },
          {
            name: '/controls/theme-settings.json',
            content: '{}'
          }
        ],
        {
          some_setting: 'showing setting'
        }
      );
      expect(result).toContain('return html`This is the translation showing setting`;');
    });
  });
});
