import { SCALAR, isScalar } from '../nodes/Node.mjs';
import { Scalar } from '../nodes/Scalar.mjs';
import { resolveBlockScalar } from './resolve-block-scalar.mjs';
import { resolveFlowScalar } from './resolve-flow-scalar.mjs';

function composeScalar(ctx, token, tagToken, onError) {
  const {
    value,
    type,
    comment,
    range
  } = token.type === 'block-scalar' ? resolveBlockScalar(token, ctx.options.strict, onError) : resolveFlowScalar(token, ctx.options.strict, onError);
  const tagName = tagToken ? ctx.directives.tagName(tagToken.source, msg => onError(tagToken, 'TAG_RESOLVE_FAILED', msg)) : null;
  const tag = tagToken && tagName ? findScalarTagByName(ctx.schema, value, tagName, tagToken, onError) : token.type === 'scalar' ? findScalarTagByTest(ctx, value, token, onError) : ctx.schema[SCALAR];
  let scalar;
  try {
    const res = tag.resolve(value, msg => onError(tagToken ?? token, 'TAG_RESOLVE_FAILED', msg), ctx.options);
    scalar = isScalar(res) ? res : new Scalar(res);
  } catch (error) {
    const msg = error instanceof Error ? error.message : String(error);
    onError(tagToken ?? token, 'TAG_RESOLVE_FAILED', msg);
    scalar = new Scalar(value);
  }
  scalar.range = range;
  scalar.source = value;
  if (type) scalar.type = type;
  if (tagName) scalar.tag = tagName;
  if (tag.format) scalar.format = tag.format;
  if (comment) scalar.comment = comment;
  return scalar;
}
function findScalarTagByName(schema, value, tagName, tagToken, onError) {
  if (tagName === '!') return schema[SCALAR]; // non-specific tag
  const matchWithTest = [];
  for (const tag of schema.tags) {
    if (!tag.collection && tag.tag === tagName) {
      if (tag.default && tag.test) matchWithTest.push(tag);else return tag;
    }
  }
  for (const tag of matchWithTest) if (tag.test?.test(value)) return tag;
  const kt = schema.knownTags[tagName];
  if (kt && !kt.collection) {
    // Ensure that the known tag is available for stringifying,
    // but does not get used by default.
    schema.tags.push(Object.assign({}, kt, {
      default: false,
      test: undefined
    }));
    return kt;
  }
  onError(tagToken, 'TAG_RESOLVE_FAILED', `Unresolved tag: ${tagName}`, tagName !== 'tag:yaml.org,2002:str');
  return schema[SCALAR];
}
function findScalarTagByTest({
  directives,
  schema
}, value, token, onError) {
  const tag = schema.tags.find(tag => tag.default && tag.test?.test(value)) || schema[SCALAR];
  if (schema.compat) {
    const compat = schema.compat.find(tag => tag.default && tag.test?.test(value)) ?? schema[SCALAR];
    if (tag.tag !== compat.tag) {
      const ts = directives.tagString(tag.tag);
      const cs = directives.tagString(compat.tag);
      const msg = `Value may be parsed as either ${ts} or ${cs}`;
      onError(token, 'TAG_RESOLVE_FAILED', msg, true);
    }
  }
  return tag;
}

export { composeScalar };
