import { NodeType } from '@hestia-earth/schema';
import * as runConfig from '../config/run-calculations.json';
import * as triggerConfig from '../config/trigger-calculations.json';
import * as Cycle from '../config/Cycle.json';
import * as ImpactAssessment from '../config/ImpactAssessment.json';
import * as Site from '../config/Site.json';

export interface IOrchestratorModelConfig {
  key: string;
  model: string;
  value: string;
  runStrategy: 'always' | 'add_key_if_missing' | 'add_blank_node_if_missing';
  runArgs?: Record<string, any>;
  mergeStrategy: 'default' | 'append' | 'list' | 'node';
  mergeArgs?: Record<string, any>;
}

export interface IOrchestratorConfig {
  models: (IOrchestratorModelConfig | IOrchestratorModelConfig[])[];
}

const allowedTypes = [NodeType.Cycle, NodeType.Site, NodeType.ImpactAssessment];

export type allowedType =
  | NodeType.Cycle
  | NodeType.Site
  | NodeType.ImpactAssessment;

const validateType = (nodeType: any) =>
  allowedTypes.includes(nodeType) ||
  (() => {
    throw new Error(
      `Invalid type ${nodeType}. Allowed types: ${allowedTypes.join(', ')}`
    );
  })();

const typeToConfig: {
  [type in NodeType]?: IOrchestratorConfig;
} = {
  [NodeType.Cycle]: Cycle as IOrchestratorConfig,
  [NodeType.ImpactAssessment]: ImpactAssessment as IOrchestratorConfig,
  [NodeType.Site]: Site as IOrchestratorConfig
};

/**
 * Load orchestrator configuration.
 *
 * @param nodeType NodeType associated with configuration.
 * @returns
 */
export const loadConfig = (nodeType: allowedType) =>
  validateType(nodeType) && typeToConfig[nodeType];

interface ICalculationConfigData {
  key: 'related' | 'nested';
  type: allowedType;
  stage: number;
}

type calculationConfigStage = {
  [stage: string]: ICalculationConfigData[];
};

type calculationConfig = {
  [type in allowedType]?: calculationConfigStage;
};

const validateStage = (
  config: calculationConfig,
  nodeType: allowedType,
  stage: number
) =>
  `stage-${stage}` in config[nodeType]
    ? config[nodeType][`stage-${stage}`]
    : (() => {
        throw new Error(
          `Invalid stage configuration for ${nodeType}: ${stage}`
        );
      })();

export const loadRunConfig = (nodeType: allowedType, stage: number) =>
  validateType(nodeType) &&
  validateStage(runConfig as calculationConfig, nodeType, stage);

export const loadTriggerConfig = (nodeType: allowedType, stage: number) =>
  validateType(nodeType) &&
  validateStage(triggerConfig as calculationConfig, nodeType, stage);

export const getMaxStage = (nodeType: allowedType) =>
  validateType(nodeType)
    ? Math.max.apply(
        Math.max,
        Object.keys((runConfig as calculationConfig)[nodeType]).map((key) =>
          Number(key.replace('stage-', ''))
        )
      )
    : null;
