import clone from 'lodash/clone';
import isNull from 'lodash/isNull';

import {kServiceDummy} from 'src/defs';
import {HomeHubBaseClass, SubscribableMixin} from 'src/lib/base';

export class ServiceState extends HomeHubBaseClass {
  static DEFAULT_PAYLOAD = {
    data: null,
    error: null,
  };
  constructor (payload) {
    super(payload);

    this.payload = {
      ...ServiceState.DEFAULT_PAYLOAD,
      ...(payload || {}),
    };
  }
  isLoading () {
    return (isNull(this.payload.data) && isNull(this.payload.error));
  }
  hasData () {
    return (
      !this.isLoading() && !isNull(this.payload.data)
    );
  }
  hasError () {
    return (
      !this.isLoading() && !isNull(this.payload.error)
    );
  }
  hasFatalError () {
    return (!this.hasData() && this.hasError());
  }
  update (nextPayload) {
    return new ServiceState({
      ...this.payload,
      ...nextPayload,
    });
  }
  data () {
    return this.payload.data;
  }
  error () {
    return this.payload.error;
  }
}

export class BaseService extends SubscribableMixin(HomeHubBaseClass) {
  static availableOffline = false;
  static availableOnline = true;
  static kind = null;
  static widget = null;
  static emptyCharacteristics () {
    return {};
  }
  constructor (spec) {
    super(spec);

    this.kind = this.constructor.kind;
    this.instance = spec.instance;
    this.characteristics = spec.characteristics;
    this.widget = this.constructor.widget;
    this.widgetComponent = spec.widgetComponent || null;
    this.layout = spec.layout;
  }
  async start () {
    throw new Error('NOT IMPLEMENTED');
  }
  async stop () {
    throw new Error('NOT IMPLEMENTED');
  }
  async restart () {
    this.notify(this.initialState(), {reset: true});

    await this.stop();
    await this.start();
  }
  onMessage (data) {
    const payload = {
      ...data,
    };

    if (data.error) {
      payload.error = new Error(data.error);
    }

    this.notify(payload);
  }
  isDummy () {
    return false;
  }
  initialState () {
    return null;
  }
  setCharacteristics (newCharacteristics) {
    this.characteristics = clone(newCharacteristics);
  }
  setLayout (newLayout) {
    this.layout = clone(newLayout);
  }
  toJSON () {
    return {
      kind: this.kind,
      instance: this.instance,
      characteristics: this.characteristics,
      layout: this.layout,
    };
  }
}

export class DummyService extends BaseService {
  kind = kServiceDummy;
  constructor (spec) {
    super({
      ...spec,
      instance: null,
      widgetComponent: null,
    });
  }
  isDummy = () => {
    return true;
  }
}

export const lookupService = (dashboards, kind, instance) => {
  let result = null;
  for (const dashboard of dashboards) {
    for (const service of dashboard.services) {
      if (service.kind === kind &&  service.instance === instance) {
        result = service;
        break;
      }
    }

    if (result !== null) {
      break;
    }
  }

  if (result === null) {
    return new DummyService();
  }

  return result;
};

export const lookupServices = (dashboards, kind) => {
  let result = [];
  for (const dashboard of dashboards) {
    for (const service of dashboard.services) {
      if (service.kind === kind) {
        result.push(service);
      }
    }
  }

  return result;
};
