import * as fs from 'fs';
import { EventEmitter } from 'events';

export const emitter = new EventEmitter();

emitter.setMaxListeners(0);

export const readyEmit = (config: DynamicObject) => {
  emitter.emit('ready', '[config] [ready] конфиг готов к работе', config);
};

export const errorEmit = (error: DynamicObject) => {
  emitter.emit('ready', '[config] [ready] произошла ошибка', error);
};

export const emit = (event: ConfigEvents, message: string, config?: DynamicObject) => {
  emitter.emit(event, `[config] [${event}] ${message}`, config);
};

// функция чтения конфига
export const readConfig = (
  path: string,
  successEmit: () => void,
  errorEmit: () => void,
  notFoundEmit: () => void,
  emptyEmit: () => void,
): DynamicObject | undefined => {
  if (fs.existsSync(path)) {
    try {
      const data = fs.readFileSync(path, { encoding: 'utf-8' });
      let parsedData: DynamicObject;

      if (!data || !JSON.parse(data)) {
        emptyEmit();
        parsedData = {};
      } else {
        parsedData = JSON.parse(data);
        successEmit();
      }

      return parsedData;
    } catch (err) {
      errorEmit();
      console.error(err);
    }
  } else {
    notFoundEmit();
  }

  return;
};

// билдер ссылки для чтения удалённого конфига
export const getRemoteDeviceConfigUrl = (url: string, id: string): string => {
  return `http://${url}/devices/config/${id}`;
};

// заготовка для функции отправки конфига на сервер
export const sendRemoteConfig = async (
  url: string,
  deviceId: string,
  config: DynamicObject,
): Promise<DynamicObject | undefined> => {
  try {
    const res = await fetch(getRemoteDeviceConfigUrl(url, deviceId), {
      method: 'PUT',
      body: JSON.stringify(config),
    });

    emit('info', 'конфиг отправлен на сервер');

    return await res.json();
  } catch (err) {
    emit('error', 'не удалось отправить конфиг на сервер');
    console.error(err);
  }

  return;
};

// клонирует объект с любым уровнем вложенности
export const cloneObject = <T>(object: T): T => JSON.parse(JSON.stringify(object));

// функция создания любого элемента HTML
export const createElement = <
  T extends keyof HTMLElementTagNameMap,
  P extends Partial<HTMLElementTagNameMap[T]>, // FIXME: убрать readonly поля
>(
  tagName: T,
  options?: P | null,
  parent?: HTMLElement | null,
  children?: HTMLElement | HTMLElement[],
): HTMLElementTagNameMap[T] => {
  const element = document.createElement(tagName);

  if (options) {
    Object.keys(options).forEach((k) => {
      const key = k as keyof P;
      (element as any)[key] = options[key];
    });
  }

  if (parent) {
    parent.appendChild(element);
  }

  if (children) {
    if (!Array.isArray(children)) {
      children = [children];
    }

    element.append(...children);
  }

  return element;
};

// читает удалённый конфиг 
export const readRemoteConfig = async (
  config: Config,
  configUrl: string,
): Promise<DynamicObject | undefined> => {
  if (!configUrl && !config.device._id) {
    emit(
      'warn',
      `не найден ${configUrl ? 'id устройства' : 'url'} для получения удаленного конфига`,
    );
    return;
  }

  if (!shouldLoadRemoteConfig(config)) {
    emit('info', 'запрос удаленного конфига отключен');
    return;
  }

  let remoteConfig: DynamicObject | undefined;

  try {
    const url = getRemoteDeviceConfigUrl(configUrl, config.device._id);
    const res = await fetch(url, { method: 'GET' });

    emit('info', 'удаленный конфиг прочитан');

    remoteConfig = await res.json();
  } catch (err) {
    emit('error', 'произошла ошибка при попытке получить удаленный конфиг');
    console.error(err);
  }

  return remoteConfig;
};

// проверяет, есть ли поле data_request_from_ad, по значению которого определяется
// и добавляет его, если оно отсутствует
// проверяет в локальном и дефолтном конфиге
const shouldLoadRemoteConfig = (config: Config) => {
  if (config.default && typeof config.default?.data_request_from_ad !== 'object') {
    config.default.data_request_from_ad = {
      name: 'Загружать конфиг с сервера',
      value: false,
    };
  }

  if (config.local && typeof config.local?.data_request_from_ad !== 'object') {
    if (config?.default?.data_request_from_ad) {
      config.local.data_request_from_ad = config.default.data_request_from_ad;
    } else {
      config.local.data_request_from_ad = {
        name: 'Загружать конфиг с сервера',
        value: false,
      };
    }
  }

  return config?.local?.data_request_from_ad?.value;
};
