import { Component, Prop, Vue } from 'vue-property-decorator';
import { TaskProcess, UserTask, WorkflowServiceApi } from '@openapi/wf';
import get from 'lodash/get';
import { isEmpty } from 'lodash';

const wfApi = new WorkflowServiceApi();

export type FormModel = Record<string | symbol, unknown>;
interface Task {
  type?: string;
  jobId?: string;
  successId?: string;
  status?: string;
  s3url?: string;
  userTask: {
    taskId?: string;
    defId?: string;
    procDefId?: string;
    formKey?: string;
    title?: string;
    createDate?: string;
    priority?: number;
    outData?: unknown;
    actions?: string[];
  };
  process?: {
    title: string;
    defKey: string;
    traceId: string;
    processTitles: [null, string];
  };
}

interface PushMessage {
  msg: string;
  type: 'success' | 'error';
  jobId?: string;
  status?: string;
  s3url?: string;
  successId?: string;
}

class SocketMessage<T> {
  id: string;
  traceId?: string;
  createDate?: string;
  till?: string;
  nextTry?: string;
  attempts?: number;
  forUser?: string;
  armID?: string;
  className?: string;
  action?: string;
  body: T;

  constructor(message: MessageEvent<string>) {
    const data = JSON.parse(get(message, 'data', '{}'));
    if (isEmpty(data)) throw new Error('');
    return {
      body: {},
      ...data,
    };
  }
}
type LocalListener = {
  (message: MessageEvent<string>): void
}

type Listener = {
  (
    message: SocketMessage<Task | PushMessage>,
    removeListener: { (): void }
  ): void;
};

export class ModuleIncorrectlyConfigured extends Error {
  constructor(message: string) {
    super(`Модуль не настроен ${message}`);
  }
}

/**
 *
 */
@Component
export default class ProcessStarter extends Vue {
  @Prop() userId!: string;
  @Prop() processKey!: string;

  @Prop({ default: () => ({}) })
  userTask: UserTask;

  @Prop({ default: () => ({}) })
  taskProcess: TaskProcess;

  listeners = new Set<LocalListener>();

  get hasParentProcess(): boolean {
    return !isEmpty(this.taskProcess) && !isEmpty(this.userTask);
  }

  public async startProcess(formModel: FormModel): Promise<string> {
    console.log('startProcess', this.processKey, formModel);
    const {
      //
      sub: userId,
      name: fullName,
      domain_id: orgId,
    } = this.$store.getters['oidcClient/currentUser'] || {};
    const orgFullName =
      this.$store.getters?.['idm/organizations/organizationsCard']?.fullName ||
      this.$store.getters?.['idm/organizations/organizationsCard']?.fullname ||
      '';
    const requestBody = {
      processKey: this.processKey,
      userId: this.userId,
      data: {
        currentUserInfo: {
          orgId,
          fullName,
          userId,
          orgFullName,
        },
        ...formModel,
      },
    };
    const { data } = await wfApi.start({
      ...requestBody,
    });
    if (!data.traceId) {
      throw new Error('Старт процесса отработал не верно');
    }
    return data.traceId;
  }

  public async submitFormToParentProcess(
    formModel: FormModel
  ): Promise<string> {
    if (!this.userTask?.taskId || !this.taskProcess.traceId) {
      throw new ModuleIncorrectlyConfigured('нет taskId и traceId');
    }
    await wfApi.submitForm(this.userTask.taskId, {
      ...formModel,
    });
    return this.taskProcess.traceId;
  }

  protected async request(formModel: FormModel): Promise<string> {
    if (this.hasParentProcess) {
      return this.submitFormToParentProcess(formModel);
    }
    return this.startProcess(formModel);
  }

  registerListener(
    cb: Listener,
    condition = (t: SocketMessage<Task | PushMessage>): boolean => true
  ): void {
    const listener = (message: MessageEvent<string>) => {
      const traceData = new SocketMessage<Task | PushMessage>(message);
      if (!condition(traceData)) return;

      const removeListener = () => {
        this.removeListener(listener);
      };

      return cb(traceData, removeListener);
    };
    this.listeners?.add(listener);
    this.$websockets.addListener(listener);
  }

  removeListener(listener: LocalListener): void {
    this.listeners?.has(listener) && this.listeners?.delete(listener);
    this.$websockets.removeListener(listener);
  }

  destroyed() {
    if (!this.listeners) return;
    for (const l of this.listeners) {
      this.removeListener(l)
    }
  }
}
