import { IGenericSection, IGenericText, IGenericFields } from '../../../generic/types';
import { ISlackSection, ISlackActions, ISlackInput, ISlackImage } from '../../types';
import { getMethods } from '../methods';

function transformSection ({ props, children }: IGenericSection): ISlackActions;
function transformSection ({ props, children }: IGenericSection): Array<ISlackSection|ISlackActions>;
function transformSection ({ props, children }: IGenericSection) {
  const flattenedSections = flattenSection(props, children);
  
  const result = flattenedSections.reduce((acc, item) => {
    const  { type, props, children } = item;
    const itemType = props?.slack?.type || type;
    
    if (itemType === 'input' && children.length === 1) { // if no label => transform as actions
      return acc.concat(getMethods('inputWrapper')({ props, children }));
    } else if (itemType === 'context' || itemType === 'header') {
      return acc.concat(getMethods(itemType)({ props, children }));
    } else if (itemType === 'actions') {
      return acc.concat(transformActions({ props, children }));
    } else if (itemType === 'fields') {
      const result = getMethods(itemType)({ props, children });
      return Array.isArray(result) 
      ? acc.concat({
            type: 'section',
            fields: result
          })
          : acc.concat(result);
        } else {
          return acc.concat(transformRegularSection({ props, children }));
        }
      }, []);
      
  const block_id = props?.slack?.block_id;
  if (block_id && result[0] && !result[0].block_id) {
    result[0].block_id = block_id;
  }

  return result;
}

// HELPER FUNCTIONS
function flat (arr) {
  if (!Array.isArray(arr)) return arr;
  return arr.reduce((acc, item) => {
    return acc.concat(flat(item));
  }, []);
}

function flattenSection (props, children) {
  const isSection = item => {
    const sectionTypes = ['section', 'context', 'header'];
    return sectionTypes.includes(item.type);
  };

  const result = [];
  let section = { props, children: [] };

  children.forEach(item => {
    if (isSection(item)) {
      if (section.children.length > 0) {
        result.push(section);
        result.push(item);
        section = { props: { type: 'section' }, children: [] };
      } else {
        result.push(item);
      }
    } else {
      section.children.push(item);
    }
  });

  if (section.children.length > 0) {
    result.push(section);
  }

  return result;
}

function transformActions ({ props, children }): ISlackActions {
  const section: ISlackActions = {
    type: 'actions',
    elements: children.map(child => getMethods(child.type)(child))
  };

  const block_id = props?.slack?.block_id;
  if (block_id) section.block_id = block_id;

  return section;
}

function isInteractiveType (type: string): boolean {
  return [
      'button',
      'checkbox',
      'datePicker',
      'radio',
      'select',
      'timePicker'
    ].includes(type);
}

function transformRegularSection ({ props, children }):  Array<ISlackSection|ISlackActions|ISlackInput> {
  const additionalSections: Array<ISlackSection|ISlackActions> = [];
  const block_id = props?.slack?.block_id;

  const result: Array<ISlackSection|ISlackActions|ISlackInput> = [];
  
  let res;

  for (let i = 0; i < children.length; i++) {
    const el = children[i];
    if (el.type === 'text') {
      if (!res) {
        res = {
          type: 'section'
        } as ISlackSection;
      } 
      if (res.type === 'section' && res.text || res.type !== 'section') {
        result.push(res);
        res = {
          type: 'section'
        };
      }
      res.text = getMethods(el.type)(el);
    } else if (isInteractiveType(el.type) || el.type === 'input') {
      const slackElement = getMethods(el.type)(el);
      if (slackElement.type === 'input') {
        res && result.push(res);
        result.push(slackElement);
        res = null;
      } else if (res?.type === 'section' && !res.accessory) {
        res.accessory = slackElement;
      } else {
        if (res?.type === 'section') {
          result.push(res);
          res = {
            type: 'actions',
            elements: []
          } as ISlackActions;
          res.elements.push(slackElement);
        } else if (res?.type === 'actions') {
          res.elements.push(slackElement);
        } else {
          res = {
            type: 'actions',
            elements: [slackElement]
          } as ISlackActions;
        }
      }
    }  else if (el.type === 'image') {
      const slackElement = getMethods(el.type)(el);
      if (!res) {
        result.push(slackElement);
      } else if (res?.type === 'section' && res.accessory || res?.type !== 'section') {
        result.push(res, slackElement);
        res = null;
      } else {
        delete slackElement.title;
        res.accessory = slackElement;
      }
    } else if (el.type === 'section') {
      const convertedSection = getMethods(el.type)(el);
      result.push(...convertedSection);
    }
  }

  if (res) {
    result.push(res);
  }

  if (block_id && result[0]) {
    result[0].block_id = block_id;
  }

  // console.log('transformRegularSection', { children, result });

  return result;

  const textObjects: Array<IGenericText> = children.filter(item => item.type === 'text');
  
  // TODO: implement fields converting
  const fields: Array<IGenericFields> = children.filter(
    item => item.type === 'fields' || item.props?.slack?.type === 'fields'
  );
  const elements = children.filter(
    item => !['text', 'fields'].includes(item.type) && !['text', 'fields'].includes(item.props?.slack?.type)
  );

  if (textObjects.length) {
    res.text = concatSectionTextObjects(textObjects);
  }

  if (fields.length) {
    const transformed = transformFieldElements(fields);
    if (transformed.fields) {
      res.fields = transformed.fields;
    }

    additionalSections.push(...transformed.sections);
  }

  if (elements.length === 1) {
    res.accessory = getMethods(elements[0].type)(elements[0]);
  } else if (elements.length > 1) {
    const actSection: ISlackActions = {
      type: 'actions',
      elements: children.map(child => getMethods(child.type)(child))
    };

    additionalSections.push(actSection);
  }

  if (!res.text && !res.fields && !res.accessory) {
    return flat(additionalSections);
  }

  return [res, ...flat(additionalSections)];
}


function concatSectionTextObjects (children, init=null) {
  const transformed = children.map(item => getMethods(item.type)(item));
  // concatenate values from all text objects
  const initial = Object.assign({ type: 'mrkdwn' }, transformed[0], { text: '' }, init);

  return transformed
    .reduce(
      (acc, item) => {
        if(acc.type === 'plain_text' && item.type === 'mrkdwn') {
          delete acc.emoji;
          acc.type = 'mrkdwn';
        }
        acc.text = `${acc.text} ${item.text}`.trim();
        return acc;
      },
      initial
    );
}

function transformFieldElements (elements) {
  const fields = Array.isArray(elements) ? [...elements] : [elements];

  const result = {
    fields: null,
    sections: []
  };
  const slackFields = fields.map(item => getMethods(item.type)(item));
  if (
    Array.isArray(slackFields[0]) &&
    ['mrkdwn', 'plain_text'].includes(slackFields[0][0].type)
  ) {
    result.fields = slackFields[0];
    if (slackFields.length > 1) {
      // create a new section for each additional field
      result.sections.push(
        ...slackFields.slice(1).map(item => {
          const section: ISlackSection = {
            type: 'section',
            fields: item
          };
          return section;
        })
      );
    }
  } else {
    result.sections.push(...slackFields);
  }
  return result;
}

export {
  transformSection
};
