// Copyright 2021 99cloud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import { inject, observer } from 'mobx-react';
import { StepAction } from 'containers/Action';
import globalIronicStore from 'stores/ironic/ironic';
import { toJS } from 'mobx';
import { isEmpty, has, isEqual } from 'lodash';
import {
  updateObjToAddSelectArray,
  updateAddSelectValueToObj,
} from 'utils/index';
import { getDifFromAddSelectValue, hasValue } from 'resources/ironic/ironic';
import DriveInterface from './DriveInterface';
import DriveInfo from './DriveInfo';
import NodeInfo from './NodeInfo';

@inject('rootStore')
@observer
export default class Create extends StepAction {
  static id = 'create';

  static title = t('Create Node');

  static path = '/compute/baremetal-node-admin/create';

  static policy = 'baremetal:node:create';

  static allowed() {
    return Promise.resolve(true);
  }

  get listUrl() {
    return this.getRoutePath('baremetalNode');
  }

  get name() {
    return this.isEdit ? t('edit baremetal node') : t('create baremetal node');
  }

  get hasConfirmStep() {
    return false;
  }

  get hasExtraProps() {
    return this.isEdit;
  }

  get steps() {
    return [
      {
        title: t('Node Info'),
        component: NodeInfo,
      },
      {
        title: t('Driver Info'),
        component: DriveInfo,
      },
      {
        title: t('Driver Interface'),
        component: DriveInterface,
      },
    ];
  }

  get isEdit() {
    const { pathname } = this.props.location;
    return pathname.indexOf('edit') >= 0;
  }

  get id() {
    const { id } = this.props.match.params;
    return id;
  }

  init() {
    this.store = globalIronicStore;
    this.getDetail();
  }

  updateItemValueToArray = (data, key) => {
    const value = data[key] || {};
    data[key] = updateObjToAddSelectArray(value);
  };

  updateDriverInfoValue = (data) => {
    const { driver_info = {} } = data;
    Object.keys(driver_info).forEach((key) => {
      data[`driver_info_${key}`] = driver_info[key];
    });
  };

  async getDetail() {
    if (this.isEdit) {
      const result = await this.store.fetchDetail({
        id: this.id,
        onlyDetail: true,
      });
      const detail = toJS(result);
      this.updateItemValueToArray(detail, 'properties');
      this.updateItemValueToArray(detail, 'extra');
      this.updateDriverInfoValue(detail);
      this.setState({
        extra: toJS(detail),
      });
    }
  }

  updateValues = (values) => {
    const { custom_trait = [], standard_trait = [], ...rest } = values;
    const traits = [];
    custom_trait.forEach((it) => traits.push(it.value));
    traits.push(...standard_trait);
    return {
      traits,
      ...rest,
    };
  };

  // eslint-disable-next-line no-unused-vars
  onSubmit = (values) => {
    const newValue = this.updateValues(values);
    if (this.isEdit) {
      return this.submitEdit(newValue);
    }
    return this.submitCreate(newValue);
  };

  submitCreate = (values) => {
    const body = {};
    const driverInfo = {};
    const { more, ...rest } = values;
    Object.keys(rest).forEach((key) => {
      const prefix = 'driver_info_';
      if (key.indexOf(prefix) === 0) {
        const newKey = key.substring(prefix.length);
        driverInfo[newKey] = values[key];
      } else if (key === 'properties' || key === 'extra') {
        const newValue = updateAddSelectValueToObj(values[key]);
        if (!isEmpty(newValue)) {
          body[key] = newValue;
        }
      } else {
        body[key] = values[key];
      }
    });
    body.driver_info = driverInfo;
    return this.store.create(body);
  };

  getKeyPath = (key) => {
    const prefix = 'driver_info_';
    if (key.indexOf(prefix) === 0) {
      return `/driver_info/${key.substring(prefix.length)}`;
    }
    return `/${key}`;
  };

  submitEdit = async (values) => {
    const adds = [];
    const replaces = [];
    const dels = [];
    const { extra: originData } = this.state;
    const { properties, extra, more, traits, ...rest } = values;
    const {
      properties: oldProps,
      extra: oldExtra,
      traits: oldTraits,
    } = originData;
    Object.keys(rest).forEach((key) => {
      const value = values[key];
      const path = this.getKeyPath(key);
      const obj = { value, path };
      if (!has(originData, key) && hasValue(value)) {
        obj.op = 'add';
        adds.push(obj);
      } else {
        const oldValue = originData[key];
        if (!isEqual(oldValue, value) && (oldValue || value)) {
          obj.op = 'replace';
          replaces.push(obj);
        }
      }
    });
    const {
      adds: pAdds,
      replaces: pReplaces,
      dels: pDels,
    } = getDifFromAddSelectValue(properties, oldProps, 'properties');
    const {
      adds: eAdds,
      replaces: eReplaces,
      dels: eDels,
    } = getDifFromAddSelectValue(extra, oldExtra, 'extra');
    adds.push(
      ...pAdds.filter((it) => hasValue(it.value)),
      ...eAdds.filter((it) => hasValue(it.value))
    );
    replaces.push(
      ...pReplaces.filter((it) => hasValue(it.value)),
      ...eReplaces.filter((it) => hasValue(it.value))
    );
    dels.push(...pDels, ...eDels);
    const body = [...adds, ...replaces, ...dels];
    if (!isEqual(traits, oldTraits)) {
      await this.store.updateTraits(this.id, traits);
    }
    if (body.length === 0) {
      return Promise.resolve();
    }
    return this.store.edit({ id: this.id }, body);
  };
}
