import { isArray, isPlainObject } from 'lodash';

import { ApplicationScope } from '../../types/application/index.js';
import { NotificationPosition } from './notification.js';
import { WidgetTypes } from './widget.js';

export type SelectedStateVarProps = {
  id: string;
  name: string;
  scope?: ApplicationScope;
};

export type SelectedEventProps = {
  id: string;
  name: string;
  scope?: ApplicationScope;
};

type SelectedWidgetProps = {
  id: string;
  name: string;
  type: WidgetTypes;
};

type SelectedTimerProps = {
  id: string;
  name: string;
  scope: ApplicationScope;
};

// Saved in the DSL. Not the same strings as what gets used in the runtime
export enum TriggerStepType {
  RUN_JS = 'runJs',
  RUN_APIS = 'executeApi',
  CANCEL_APIS = 'cancelApi',
  NAVIGATE_TO = 'navigateTo',
  NAVIGATE_TO_APP = 'navigateToApp',
  NAVIGATE_TO_ROUTE = 'navigateToRoute',
  SET_QUERY_PARAMS = 'setQueryParams',
  CONTROL_SLIDEOUT = 'controlSlideout',
  CONTROL_MODAL = 'controlModal',
  CONTROL_TIMER = 'controlTimer',
  RESET_COMPONENT = 'resetComponent',
  RESET_STATE_VAR = 'resetStateVar',
  SET_STATE_VAR = 'setStateVar',
  SET_COMPONENT_PROPERTY = 'setComponentProperty',
  SHOW_ALERT = 'showAlert',
  SET_PROFILE = 'setProfile',
  TRIGGER_EVENT = 'triggerEvent',
  RUN_SUPERBLOCKS_OPTIMIZED_APIS = 'optimizedPageLoadApis'
}

// react-toastify type options
type TypeOptions = 'info' | 'success' | 'warning' | 'error' | 'default';

export type ValidStepDef =
  | {
      id: string;
      type: TriggerStepType.RUN_APIS;
      apiNames: string[];
      onSuccess: MultiStepDef;
      onError: MultiStepDef;
    }
  | {
      id: string;
      type: TriggerStepType.RUN_SUPERBLOCKS_OPTIMIZED_APIS;
    }
  | {
      id: string;
      type: TriggerStepType.CANCEL_APIS;
      apiNames: string[];
      onCancel?: MultiStepDef;
    }
  | {
      id: string;
      type: TriggerStepType.RUN_JS;
      code?: string;
    }
  | {
      id: string;
      type: TriggerStepType.NAVIGATE_TO;
      url: string;
      newWindow: boolean;
      replaceHistory?: boolean;
      // TODO: This is currently unused, needs to be added to the form
      arguments: string;
    }
  | {
      id: string;
      type: TriggerStepType.NAVIGATE_TO_APP;
      targetApp: {
        name: string;
        id: string;
        // TODO: this url can change overtime (if default page is changed) but getting it dynamiclly on navigate might take extra time
        // Ideally, we should be able to redirect to url with only applicatoinId to app with default page
        url: string;
      };
      queryParams: string; // this string is parsed at execution time
      newWindowApp: boolean;
    }
  | {
      id: string;
      type: TriggerStepType.NAVIGATE_TO_ROUTE;
      routeId: string;
      // only used to tell the user there might be an error (i.e. deleted routed), you never want to use this for routing
      routePathDescriptor: string;
      routeParams?: Record<string, string>;
      queryParams?: string; // parsed at execution
      keepQueryParams?: boolean;
      newWindow: boolean;
    }
  | {
      id: string;
      // we use URL_PARAMS as our identifier because it might include more than query params in the future
      type: TriggerStepType.SET_QUERY_PARAMS;
      queryParams?: string; // parsed at execution
      keepQueryParams?: boolean;
    }
  | {
      id: string;
      type: TriggerStepType.CONTROL_SLIDEOUT;
      name: string;
      direction: 'open' | 'close';
    }
  | {
      id: string;
      type: TriggerStepType.CONTROL_MODAL;
      name: string;
      direction: 'open' | 'close';
    }
  | {
      id: string;
      type: TriggerStepType.CONTROL_TIMER;
      name: string;
      command: 'start' | 'stop' | 'toggle';
      // optional, because timers without state are pre-AppScope and are defaulted to Page scope
      state?: SelectedTimerProps;
    }
  | {
      id: string;
      type: TriggerStepType.SET_STATE_VAR;
      state: SelectedStateVarProps; // TODO: this should be optional
      value: string; // TODO: this should be optional
    }
  | {
      id: string;
      type: TriggerStepType.RESET_STATE_VAR;
      state: SelectedStateVarProps; // TODO: this should be optional
    }
  | {
      id: string;
      type: TriggerStepType.RESET_COMPONENT;
      widget: SelectedWidgetProps;
      propertyName: string;
      resetChildren: boolean;
    }
  | {
      id: string;
      type: TriggerStepType.SHOW_ALERT;
      message: string;
      style: TypeOptions;
      alertDuration?: number;
      alertPosition?: NotificationPosition;
    }
  | {
      id: string;
      type: TriggerStepType.SET_COMPONENT_PROPERTY;
      widget: SelectedWidgetProps;
      propertyName: string;
      propertyValue: string;
    }
  | {
      id: string;
      type: TriggerStepType.SET_PROFILE;
      profileId: string;
      profileAction: 'set' | 'unset';
    }
  | {
      id: string;
      type: TriggerStepType.TRIGGER_EVENT;
      event: SelectedEventProps;
      eventPayload: Record<string, string>; // map of argument id to value
    };

export type StepDef =
  | {
      id: string;
    }
  | ValidStepDef;

export type MultiStepDef = StepDef[];
export type ValidMultiStepDef = ValidStepDef[];

export type EventArg = {
  id: string;
  name: string;
};

export type EventDefinition = {
  id: string;
  name: string;
  arguments: EventArg[];
  onTrigger: MultiStepDef;
};

export type EventDefinitionLoose = Omit<EventDefinition, 'onTrigger'> & {
  // on FE, we are not cleaning irrelavant fields when switching action type, so using unknown to avoid schema check failure
  onTrigger: unknown[];
};

export function isValidStepDef(step: unknown): step is ValidStepDef {
  if (isPlainObject(step)) {
    return 'type' in (step as Record<string, unknown>);
  }
  return false;
}

export function isValidMultiStepDef(steps: unknown[]): steps is ValidMultiStepDef {
  if (isArray(steps)) {
    return steps.every(isValidStepDef);
  }
  return false;
}
