const kZenStylesheetThemeHeader = '/* Zen Themes - Generated by ZenThemesImporter.';
const kZenStylesheetThemeHeaderBody = `* DO NOT EDIT THIS FILE DIRECTLY!
* Your changes will be overwritten.
* Instead, go to the preferences and edit the themes there.
*/
`;
const kenStylesheetFooter = `
/* End of Zen Themes */
`;
const getCurrentDateTime = () =>
  new Intl.DateTimeFormat('en-US', {
    dateStyle: 'full',
    timeStyle: 'full',
  }).format(new Date().getTime());

var gZenStylesheetManager = {
  async writeStylesheet(path, themes) {
    let content = kZenStylesheetThemeHeader;
    content += `\n* FILE GENERATED AT: ${getCurrentDateTime()}\n`;
    content += kZenStylesheetThemeHeaderBody;

    for (let theme of themes) {
      if (theme.enabled !== undefined && !theme.enabled) {
        continue;
      }

      content += this.getThemeCSS(theme);
    }

    content += kenStylesheetFooter;

    const buffer = new TextEncoder().encode(content);

    await IOUtils.write(path, buffer);
  },

  getThemeCSS(theme) {
    let css = '\n';

    css += `/* Name: ${theme.name} */\n`;
    css += `/* Description: ${theme.description} */\n`;
    css += `/* Author: @${theme.author} */\n`;

    if (theme._readmeURL) {
      css += `/* Readme: ${theme.readme} */\n`;
    }

    css += `@import url("${theme._chromeURL}");\n`;

    return css;
  },
};

var gZenThemesImporter = new (class {
  constructor() {
    try {
      window.SessionStore.promiseInitialized.then(async () => {
        if (
          Services.prefs.getBoolPref('zen.themes.disable-all', false) ||
          Services.appinfo.inSafeMode
        ) {
          console.log('[ZenThemesImporter]: Disabling all themes.');
          return;
        }

        const themes = await this.getEnabledThemes();

        const themesWithPreferences = await Promise.all(
          themes.map(async (theme) => {
            const preferences = await ZenThemesCommon.getThemePreferences(theme);

            return {
              name: theme.name,
              enabled: theme.enabled,
              preferences,
            };
          })
        );

        this.writeToDom(themesWithPreferences);

        await this.insertStylesheet();
      });
      console.info('[ZenThemesImporter]: Zen Themes imported');
    } catch (e) {
      console.error('[ZenThemesImporter]: Error importing Zen Themes: ', e);
    }

    Services.prefs.addObserver(
      'zen.themes.updated-value-observer',
      this.rebuildThemeStylesheet.bind(this),
      false
    );
    Services.prefs.addObserver(
      'zen.themes.disable-all',
      this.handleDisableThemes.bind(this),
      false
    );
  }

  get sss() {
    if (!this._sss) {
      this._sss = Cc['@mozilla.org/content/style-sheet-service;1'].getService(
        Ci.nsIStyleSheetService
      );
    }
    return this._sss;
  }

  get styleSheetPath() {
    return PathUtils.join(PathUtils.profileDir, 'chrome', 'zen-themes.css');
  }

  async handleDisableThemes() {
    if (Services.prefs.getBoolPref('zen.themes.disable-all', false)) {
      console.log('[ZenThemesImporter]: Disabling themes module.');

      await this.removeStylesheet();
    } else {
      console.log('[ZenThemesImporter]: Enabling themes module.');

      await this.rebuildThemeStylesheet();
    }
  }

  get styleSheetURI() {
    if (!this._styleSheetURI) {
      this._styleSheetURI = Services.io.newFileURI(new FileUtils.File(this.styleSheetPath));
    }
    return this._styleSheetURI;
  }

  getStylesheetURIForTheme(theme) {
    return Services.io.newFileURI(
      new FileUtils.File(PathUtils.join(ZenThemesCommon.getThemeFolder(theme.id), 'chrome.css'))
    );
  }

  async insertStylesheet() {
    if (await IOUtils.exists(this.styleSheetPath)) {
      await this.sss.loadAndRegisterSheet(this.styleSheetURI, this.sss.AGENT_SHEET);
    }

    if (this.sss.sheetRegistered(this.styleSheetURI, this.sss.AGENT_SHEET)) {
      console.debug('[ZenThemesImporter]: Sheet successfully registered');
    }
  }

  async removeStylesheet() {
    await this.sss.unregisterSheet(this.styleSheetURI, this.sss.AGENT_SHEET);
    await IOUtils.remove(this.styleSheetPath, { ignoreAbsent: true });

    if (
      !this.sss.sheetRegistered(this.styleSheetURI, this.sss.AGENT_SHEET) &&
      !(await IOUtils.exists(this.styleSheetPath))
    ) {
      console.debug('[ZenThemesImporter]: Sheet successfully unregistered');
    }
  }

  async rebuildThemeStylesheet() {
    ZenThemesCommon.themes = null;

    await this.removeStylesheet();

    const themes = await this.getEnabledThemes();

    await this.writeStylesheet(themes);

    const themesWithPreferences = await Promise.all(
      themes.map(async (theme) => {
        const preferences = await ZenThemesCommon.getThemePreferences(theme);

        return {
          name: theme.name,
          enabled: theme.enabled,
          preferences,
        };
      })
    );

    this.setDefaults(themesWithPreferences);
    this.writeToDom(themesWithPreferences);

    await this.insertStylesheet();
  }

  async getEnabledThemes() {
    const themeObject = await ZenThemesCommon.getThemes();
    const themes = Object.values(themeObject).filter(
      (theme) => theme.enabled === undefined || theme.enabled
    );

    const themeList = themes.map(({ name }) => name).join(', ');

    const message =
      themeList !== ''
        ? `[ZenThemesImporter]: Loading enabled Zen themes: ${themeList}.`
        : '[ZenThemesImporter]: No enabled Zen themes.';

    console.log(message);

    return themes;
  }

  setDefaults(themesWithPreferences) {
    for (const { preferences, enabled } of themesWithPreferences) {
      if (enabled !== undefined && !enabled) {
        continue;
      }

      for (const { type, property, defaultValue } of preferences) {
        if (defaultValue === undefined) {
          continue;
        }

        switch (type) {
          case 'checkbox': {
            const value = Services.prefs.getBoolPref(property, false);
            if (typeof defaultValue !== 'boolean') {
              console.log(
                `[ZenThemesImporter]: Warning, invalid data type received for expected type boolean, skipping.`
              );
              continue;
            }

            if (!value) {
              Services.prefs.setBoolPref(property, defaultValue);
            }
            break;
          }

          default: {
            const value = Services.prefs.getStringPref(property, 'zen-property-no-saved');

            if (typeof defaultValue !== 'string' && typeof defaultValue !== 'number') {
              console.log(
                `[ZenThemesImporter]: Warning, invalid data type received (${typeof defaultValue}), skipping.`
              );
              continue;
            }

            if (value === 'zen-property-no-saved') {
              Services.prefs.setStringPref(property, defaultValue.toString());
            }
          }
        }
      }
    }
  }

  writeToDom(themesWithPreferences) {
    for (const browser of ZenMultiWindowFeature.browsers) {
      for (const { enabled, preferences, name } of themesWithPreferences) {
        const sanitizedName = `theme-${name?.replaceAll(/\s/g, '-')?.replaceAll(/[^A-z_-]+/g, '')}`;

        if (enabled !== undefined && !enabled) {
          const element = browser.document.getElementById(sanitizedName);

          if (element) {
            element.remove();
          }

          for (const { property } of preferences.filter(({ type }) => type !== 'checkbox')) {
            const sanitizedProperty = property?.replaceAll(/\./g, '-');

            browser.document.querySelector(':root').style.removeProperty(`--${sanitizedProperty}`);
          }

          continue;
        }

        for (const { property, type } of preferences) {
          const value = Services.prefs.getStringPref(property, '');
          const sanitizedProperty = property?.replaceAll(/\./g, '-');

          switch (type) {
            case 'dropdown': {
              if (value !== '') {
                let element = browser.document.getElementById(sanitizedName);

                if (!element) {
                  element = browser.document.createElement('div');

                  element.style.display = 'none';
                  element.setAttribute('id', sanitizedName);

                  browser.document.body.appendChild(element);
                }

                element.setAttribute(sanitizedProperty, value);
              }
              break;
            }

            case 'string': {
              if (value === '') {
                browser.document
                  .querySelector(':root')
                  .style.removeProperty(`--${sanitizedProperty}`);
              } else {
                browser.document
                  .querySelector(':root')
                  .style.setProperty(`--${sanitizedProperty}`, value);
              }
              break;
            }

            default: {
            }
          }
        }
      }
    }
  }

  async writeStylesheet(themeList = []) {
    const themes = [];
    ZenThemesCommon.themes = null;

    for (let theme of themeList) {
      theme._chromeURL = this.getStylesheetURIForTheme(theme).spec;
      themes.push(theme);
    }

    await gZenStylesheetManager.writeStylesheet(this.styleSheetPath, themes);
  }
})();

gZenActorsManager.addJSWindowActor('ZenThemeMarketplace', {
  parent: {
    esModuleURI: 'chrome://browser/content/zen-components/actors/ZenThemeMarketplaceParent.sys.mjs',
  },
  child: {
    esModuleURI: 'chrome://browser/content/zen-components/actors/ZenThemeMarketplaceChild.sys.mjs',
    events: {
      DOMContentLoaded: {},
    },
  },
  matches: [
    ...Services.prefs.getStringPref('zen.injections.match-urls').split(','),
    'about:preferences',
  ],
});
