import { action, makeObservable, observable, runInAction, toJS } from 'mobx';
import { loadable, loadableCard } from './Decorators/LoadingDecorator';
import { environment } from '../enviroments/enviroments';
import request from '../services/request';

// Types
import { DevicesRecord } from '../types/default';
import { Entity, EntityStore } from './EntityStore';
import { RootStore } from './RootStore';
import {
  ArmaIndustrialFirewall,
  CreateSensorDevice,
  EndpointDevice,
  SensorDevice,
} from '../myApi';
import { TypeUploadedIdsRules } from '../pages/DevicesDashboard';

import { DownloadFile } from '../services/FileDownload';

type ListRecord = DevicesRecord;

export class Device extends Entity {
  @observable private listRecord: ListRecord;

  constructor(id: string, listRecord: ListRecord) {
    super(id);
    this.listRecord = listRecord;
  }

  get _listRecord(): any {
    return this.listRecord;
  }

  @action
  setListRecord = (value: ListRecord): any => {
    this.listRecord = value;
  };
}

export class DevicesStore extends EntityStore<Device> {
  @observable countDevices: number | undefined;

  @observable sensorInformationData: SensorDevice | null = null;

  @observable antivirusEvents: any;

  @observable antivirusEventsCount = 0;

  constructor(private readonly rootStore: RootStore) {
    super();
    makeObservable(this);
  }

  // Общие функции для IE и IF

  // ПОлучение списка устройств
  @loadable async fetchEventSourceDevices(
    page: number,
    pageSize: number,
    sorterField = '',
  ): Promise<void> {
    const response = await request({
      method: 'get',
      url: `${environment.devices.list}?page=${page}&page_size=${pageSize}${sorterField}`,
    });

    runInAction(() => {
      this.entities.clear();
      for (const item of response.data.results) {
        const device = new Device(item.id, item);
        device.setListRecord({
          ...item,
        });
        this.entities.set(item.id, device);
      }
      this.setTotal(response.data.count);
    });
  }

  // Удаление устройства по id
  @loadable async deleteDeviceRecord(ids: number[]): Promise<void> {
    const myPromise = (id: number): Promise<number> => {
      return new Promise((resolve, reject) => {
        return request({
          method: 'delete',
          url: `${environment.devices.list}${id}/`,
        })
          .then(() => resolve(id))
          .catch(() => reject(id));
      });
    };

    const promiseArray = ids.map((item) => {
      return myPromise(item);
    });

    await Promise.allSettled(promiseArray);
  }

  // Экспорт таблицы
  async exportCSV(): Promise<void> {
    await DownloadFile(environment.exportsCSV.devices);
  }

  // Функции для IF  ------------------------------------------------
  // Редактирование IF
  async patchFirewallRecord(
    data: ArmaIndustrialFirewall,
    id: number,
  ): Promise<void> {
    const response = await request({
      method: 'patch',
      url: `${environment.devices.list}${environment.devices.firewall}${id}/`,
      data,
    });

    runInAction(() => {
      const newData = { ...response.data, type: 'firewall' };
      const newRecord = new Device(response.data.id, newData);
      this.entities.set(newRecord.id as number, newRecord);
    });
  }

  // Создание IF
  @loadableCard
  async postFirewallRecord(data: ArmaIndustrialFirewall): Promise<void> {
    const response = await request({
      method: 'post',
      url: `${environment.devices.list}${environment.devices.firewall}`,
      data,
    });

    runInAction(() => {
      const newData = { ...response.data, type: 'firewall' };
      const newRecord = new Device(response.data.id, newData);
      this.entities.set(newRecord.id as number, newRecord);
    });
  }

  // Загрузка конфигурации IF
  async downloadConfigFirewallId(ids: number[]): Promise<void> {
    ids.forEach((id) => {
      return DownloadFile(
        `${environment.devices.list}${environment.devices.firewall}${id}/${environment.devices.downloadConfig}`,
      );
    });
  }

  // Обновление конфигурации IF
  async uploadConfigOnFirewall(
    ids: number[],
    data: FormData,
  ): Promise<PromiseSettledResult<unknown>[]> {
    const myPromise = (id: number): Promise<number> => {
      return new Promise((resolve, reject) => {
        return request({
          method: 'post',
          url: `${environment.devices.list}${environment.devices.firewall}${id}/${environment.devices.uploadConfig}`,
          data,
        })
          .then(() => resolve(id))
          .catch(() => reject(id));
      });
    };

    const promiseArray = ids.map((item) => {
      return myPromise(item);
    });

    return Promise.allSettled(promiseArray);
  }

  // Обновление правил СОВ (IDS) IF
  async uploadRulesFirewall(
    ids: number[],
    data: FormData,
  ): Promise<PromiseSettledResult<TypeUploadedIdsRules>[]> {
    const myPromise = (id: number): Promise<TypeUploadedIdsRules> => {
      return new Promise((resolve, reject) => {
        return request({
          method: 'post',
          url: `${environment.devices.list}${environment.devices.firewall}${id}/${environment.devices.uploadRules}`,
          data,
        })
          .then((response) => resolve({ id, response: response.data }))
          .catch((reason) => reject({ id, reason }));
      });
    };

    const promiseArray = ids.map((item) => {
      return myPromise(item);
    });

    return Promise.allSettled(promiseArray);
  }

  // Перезагрузка IF
  async rebootFirewall(id: number): Promise<void> {
    await request({
      method: 'post',
      url: `devices/firewall/${id}/reboot/`,
    });
  }

  // Функции для IE   ------------------------------------------------
  // Редактирование IE
  @loadableCard
  async patchEndpointRecord(data: EndpointDevice, id: number): Promise<void> {
    const response = await request({
      method: 'patch',
      url: `${environment.devices.endpoint}${id}/`,
      data,
    });

    runInAction(() => {
      const newData = {
        ...response.data,
        type: 'endpoint',
        status: response.data.status.status,
      };
      const newRecord = new Device(response.data.id, newData);
      this.entities.set(newRecord.id as number, newRecord);
    });
  }

  // Загрузка конфигурации IE
  async downloadConfigEndpoint(id: number): Promise<void> {
    await DownloadFile(`/endpoint/${id}/download/?format=api`);
  }

  // Обновление конфигурации с IE
  uploadConfigEndpoint(
    ids: number[],
  ): Promise<PromiseSettledResult<unknown>[]> {
    const myPromise = (id: number): Promise<number> => {
      return new Promise((resolve, reject) => {
        return request({
          method: 'get',
          url: `/endpoint/${id}/config_request/`,
        })
          .then(() => resolve(id))
          .catch(() => reject(id));
      });
    };

    const promiseArray = ids.map((item) => {
      return myPromise(item);
    });

    return Promise.allSettled(promiseArray);
  }

  // Создание IE
  @loadableCard
  async postEndpointRecord(data: EndpointDevice): Promise<void> {
    const response = await request({
      method: 'post',
      url: `${environment.devices.endpoint}`,
      data,
    });

    runInAction(() => {
      const newData = {
        ...response.data,
        type: 'endpoint',
      };
      const newRecord = new Device(response.data.id, newData);
      this.entities.set(newRecord.id as number, newRecord);
    });
  }

  // Создание IS
  async createSensorDevice(data: CreateSensorDevice): Promise<void> {
    const response = await request<CreateSensorDevice>({
      method: 'post',
      url: `/devices/sensor/`,
      data,
    });

    runInAction(() => {
      const newData = {
        ...response.data,
        type: 'sensor',
      };
      const newRecord = new Device(response.data.id, newData);
      this.entities.set(newRecord.id as number, newRecord);
      this.sensorInformationData = response.data;
    });
  }

  async editSensorDevice(data: EndpointDevice, id: number): Promise<void> {
    const response = await request({
      method: 'patch',
      url: `/devices/sensor/${id}/`,
      data,
    });

    runInAction(() => {
      const newData = {
        ...response.data,
        type: 'sensor',
        status: response.data.status.status,
      };
      const newRecord = new Device(response.data.id, newData);
      this.entities.set(newRecord.id as number, newRecord);
      this.sensorInformationData = response.data;
    });
  }

  get getSensorInformation(): SensorDevice | null {
    return toJS(this.sensorInformationData);
  }

  // Получение данных антивируса для IE в большой карточке
  @loadableCard
  async fetchAntivirusEvents(
    endpoint: number,
    page: number,
    pageSize: number,
  ): Promise<void> {
    const response = await request({
      method: 'get',
      url: `/events/elastic/query/aggregated-*?page=${page}&page_size=${pageSize}&q=type:endpoint_${endpoint}&sign_category:Antivirus/`,
    });

    runInAction(() => {
      this.antivirusEventsCount = response.data.hits.total.value;
      this.antivirusEvents = response.data.hits.hits;
    });
  }
}
