import { isNode, isCollection, isScalar, isSeq } from '../nodes/identity.mjs';
import { Scalar } from '../nodes/Scalar.mjs';
import { stringify } from './stringify.mjs';
import { lineComment, indentComment } from './stringifyComment.mjs';

function stringifyPair({
  key,
  value
}, ctx, onComment, onChompKeep) {
  const {
    allNullValues,
    doc,
    indent,
    indentStep,
    options: {
      commentString,
      indentSeq,
      simpleKeys
    }
  } = ctx;
  let keyComment = isNode(key) && key.comment || null;
  if (simpleKeys) {
    if (keyComment) {
      throw new Error('With simple keys, key nodes cannot have comments');
    }
    if (isCollection(key) || !isNode(key) && typeof key === 'object') {
      const msg = 'With simple keys, collection cannot be used as a key value';
      throw new Error(msg);
    }
  }
  let explicitKey = !simpleKeys && (!key || keyComment && value == null && !ctx.inFlow || isCollection(key) || (isScalar(key) ? key.type === Scalar.BLOCK_FOLDED || key.type === Scalar.BLOCK_LITERAL : typeof key === 'object'));
  ctx = Object.assign({}, ctx, {
    allNullValues: false,
    implicitKey: !explicitKey && (simpleKeys || !allNullValues),
    indent: indent + indentStep
  });
  let keyCommentDone = false;
  let chompKeep = false;
  let str = stringify(key, ctx, () => keyCommentDone = true, () => chompKeep = true);
  if (!explicitKey && !ctx.inFlow && str.length > 1024) {
    if (simpleKeys) throw new Error('With simple keys, single line scalar must not span more than 1024 characters');
    explicitKey = true;
  }
  if (ctx.inFlow) {
    if (allNullValues || value == null) {
      if (keyCommentDone && onComment) onComment();
      return str === '' ? '?' : explicitKey ? `? ${str}` : str;
    }
  } else if (allNullValues && !simpleKeys || value == null && explicitKey) {
    str = `? ${str}`;
    if (keyComment && !keyCommentDone) {
      str += lineComment(str, ctx.indent, commentString(keyComment));
    } else if (chompKeep && onChompKeep) onChompKeep();
    return str;
  }
  if (keyCommentDone) keyComment = null;
  if (explicitKey) {
    if (keyComment) str += lineComment(str, ctx.indent, commentString(keyComment));
    str = `? ${str}\n${indent}:`;
  } else {
    str = `${str}:`;
    if (keyComment) str += lineComment(str, ctx.indent, commentString(keyComment));
  }
  let vsb, vcb, valueComment;
  if (isNode(value)) {
    vsb = !!value.spaceBefore;
    vcb = value.commentBefore;
    valueComment = value.comment;
  } else {
    vsb = false;
    vcb = null;
    valueComment = null;
    if (value && typeof value === 'object') value = doc.createNode(value);
  }
  ctx.implicitKey = false;
  if (!explicitKey && !keyComment && isScalar(value)) ctx.indentAtStart = str.length + 1;
  chompKeep = false;
  if (!indentSeq && indentStep.length >= 2 && !ctx.inFlow && !explicitKey && isSeq(value) && !value.flow && !value.tag && !value.anchor) {
    // If indentSeq === false, consider '- ' as part of indentation where possible
    ctx.indent = ctx.indent.substring(2);
  }
  let valueCommentDone = false;
  const valueStr = stringify(value, ctx, () => valueCommentDone = true, () => chompKeep = true);
  let ws = ' ';
  if (keyComment || vsb || vcb) {
    ws = vsb ? '\n' : '';
    if (vcb) {
      const cs = commentString(vcb);
      ws += `\n${indentComment(cs, ctx.indent)}`;
    }
    if (valueStr === '' && !ctx.inFlow) {
      if (ws === '\n') ws = '\n\n';
    } else {
      ws += `\n${ctx.indent}`;
    }
  } else if (!explicitKey && isCollection(value)) {
    const vs0 = valueStr[0];
    const nl0 = valueStr.indexOf('\n');
    const hasNewline = nl0 !== -1;
    const flow = ctx.inFlow ?? value.flow ?? value.items.length === 0;
    if (hasNewline || !flow) {
      let hasPropsLine = false;
      if (hasNewline && (vs0 === '&' || vs0 === '!')) {
        let sp0 = valueStr.indexOf(' ');
        if (vs0 === '&' && sp0 !== -1 && sp0 < nl0 && valueStr[sp0 + 1] === '!') {
          sp0 = valueStr.indexOf(' ', sp0 + 1);
        }
        if (sp0 === -1 || nl0 < sp0) hasPropsLine = true;
      }
      if (!hasPropsLine) ws = `\n${ctx.indent}`;
    }
  } else if (valueStr === '' || valueStr[0] === '\n') {
    ws = '';
  }
  str += ws + valueStr;
  if (ctx.inFlow) {
    if (valueCommentDone && onComment) onComment();
  } else if (valueComment && !valueCommentDone) {
    str += lineComment(str, ctx.indent, commentString(valueComment));
  } else if (chompKeep && onChompKeep) {
    onChompKeep();
  }
  return str;
}

export { stringifyPair };
