import * as defaultConfig from "@/assets/config.json";

export class Config {
  // Configuration file entries
  //
  appName: string = "";
  appNames: { [key: string]: string } = {};
  languageSupportEmail: string = "";
  productLink: string = "";
  defaultBaseUrl: string = "";
  matrixDomainPartMapping: { [key: string]: string | string[] } = {};
  useFullyQualifiedDMLinks: boolean = true;
  defaultMatrixDomainPart: string = "";
  identityServer?: string;
  registrationToken?: string;
  userIdPrefix?: string;
  accentColor?: string;
  logo?: string;
  analytics: { type?: string; enabled: boolean; config?: any }[] = [];
  experimental_voice_mode: boolean = true;
  experimental_file_mode: boolean = true;
  experimental_read_only_room: boolean = true;
  experimental_public_room: boolean = true;
  show_status_messages: "always" | "never" | "moderators" = "never";
  hide_add_room_on_home: boolean = false;
  mirrors?: string[];
  maxSizeAutoDownloads: number = 10 * 1024 * 1024;
  disableMediaSharing: boolean = false;
  chat_backgrounds: { all?: string[]; direct?: string[]; public?: string[]; private?: string[] } = {};
  shortCodeStickers?: { packs: { name: string; stickers: string[] }[] };
  roomTypes: ("group_chat" | "channel" | "file_drop")[] = ["group_chat", "channel", "file_drop"];


  // Class implementation
  //
  _loaded: boolean;
  _loadPromise: Promise<Config> | undefined;

  constructor() {
    this._loaded = false;
  }

  load = (): Promise<Config> => {
    if (this._loadPromise) return this._loadPromise;
    this._loadPromise = fetch("./config.json?ms=" + Date.now())
      .then((res) => res.json())
      .catch((err) => {
        console.error("Failed to get config:", err);
        return {};
      })
      .then((json: { [key: string]: any }) => {
        this.fromJson(json);
        this._loaded = true;
        return this;
      });
    return this._loadPromise;
  };

  isLoaded = () => {
    return this._loaded;
  };

  fromJson = (json: { [key: string]: any }) => {
    for (const key of Object.keys(json)) {
      if (!Object.hasOwn(this, key)) {
        console.warn("Invalid config value", key);
      } else {
        (this as any)[key] = json[key];
      }
    }
  };

  /**
   * If there is an explicit mapping for this MX domain in the config file, return the endpoint URL that it maps to.
   * @param {*} domain
   * @returns
   */
  getMatrixDomainPartMapping = (domain: string) => {
    console.log("Get domain endpoint mapping for", domain);
    if (this.matrixDomainPartMapping && this.matrixDomainPartMapping[domain]) {
      const mapping = this.matrixDomainPartMapping[domain];
      if (Array.isArray(mapping)) {
        return mapping[0]; //TODO - Use the first one for now, but maybe rotate somehow?
      }
      return mapping;
    }
    return undefined;
  };
}

export default {
  install(app: any, defaultServerFromLocation: string, onloaded: (config: Config) => void) {
    var config = new Config();
    config.defaultBaseUrl = defaultServerFromLocation;
    config.fromJson(defaultConfig.default);

    config.load().then((config: Config) => {
      document.title = config.appName;

      // Tell callback we are done loading runtime config
      if (onloaded) {
        onloaded(config);
      }
      return config;
    });

    // Proxy the object for debugging
    const handler = {
      get(target: any, prop: string, receiver: any) {
        if (!Object.hasOwn(target, prop)) {
          console.error("CONFIG - Invalid property", prop);
        }
        return Reflect.get(target, prop, receiver);
      },
    };
    const configProxy = new Proxy(config, handler);

    app.$config = configProxy;
    app.config.globalProperties.$config = configProxy;
  },
};
