/**
 * @since 4.0.0
 */
import * as Arr from "./Array.js";
import * as Cause from "./Cause.js";
import * as Effect from "./Effect.js";
import * as Exit from "./Exit.js";
import { identity, memoize } from "./Function.js";
import * as InternalAnnotations from "./internal/schema/annotations.js";
import * as Option from "./Option.js";
import * as Predicate from "./Predicate.js";
import * as Result from "./Result.js";
import * as AST from "./SchemaAST.js";
import * as Issue from "./SchemaIssue.js";
const recurDefaults = /*#__PURE__*/memoize(ast => {
  switch (ast._tag) {
    case "Declaration":
      {
        const getLink = ast.annotations?.[AST.ClassTypeId];
        if (Predicate.isFunction(getLink)) {
          const link = getLink(ast.typeParameters);
          const to = recurDefaults(link.to);
          return AST.replaceEncoding(ast, to === link.to ? [link] : [new AST.Link(to, link.transformation)]);
        }
        return ast;
      }
    case "Objects":
    case "Arrays":
      return ast.recur(ast => {
        const defaultValue = ast.context?.defaultValue;
        if (defaultValue) {
          return AST.replaceEncoding(recurDefaults(ast), defaultValue);
        }
        return recurDefaults(ast);
      });
    case "Suspend":
      return ast.recur(recurDefaults);
    default:
      return ast;
  }
});
/**
 * @category Constructing
 * @since 4.0.0
 */
export function makeEffect(schema) {
  const ast = recurDefaults(AST.toType(schema.ast));
  const parser = run(ast);
  return (input, options) => {
    return parser(input, options?.disableChecks ? options?.parseOptions ? {
      ...options.parseOptions,
      disableChecks: true
    } : {
      disableChecks: true
    } : options?.parseOptions);
  };
}
/**
 * @category Constructing
 * @since 4.0.0
 */
export function makeOption(schema) {
  const parser = makeEffect(schema);
  return (input, options) => {
    return Exit.getSuccess(Effect.runSyncExit(parser(input, options)));
  };
}
/**
 * @category Constructing
 * @since 4.0.0
 */
export function makeUnsafe(schema) {
  const parser = makeEffect(schema);
  return (input, options) => {
    return Effect.runSync(Effect.mapErrorEager(parser(input, options), issue => new Error(issue.toString(), {
      cause: issue
    })));
  };
}
/**
 * @category Asserting
 * @since 4.0.0
 */
export function is(schema) {
  return _is(schema.ast);
}
/** @internal */
export function _is(ast) {
  const parser = asExit(run(AST.toType(ast)));
  return input => {
    return Exit.isSuccess(parser(input, AST.defaultParseOptions));
  };
}
/** @internal */
export function _issue(ast) {
  const parser = run(ast);
  return (input, options) => {
    return Effect.runSync(Effect.matchEager(parser(input, options), {
      onSuccess: () => undefined,
      onFailure: identity
    }));
  };
}
/**
 * @category Asserting
 * @since 4.0.0
 */
export function asserts(schema) {
  const parser = asExit(run(AST.toType(schema.ast)));
  return input => {
    const exit = parser(input, AST.defaultParseOptions);
    if (Exit.isFailure(exit)) {
      const issue = Cause.findError(exit.cause);
      if (Result.isFailure(issue)) {
        throw Cause.squash(issue.failure);
      }
      throw new Error(issue.success.toString(), {
        cause: issue.success
      });
    }
  };
}
/**
 * @category Decoding
 * @since 4.0.0
 */
export function decodeUnknownEffect(schema) {
  return run(schema.ast);
}
/**
 * @category Decoding
 * @since 4.0.0
 */
export const decodeEffect = decodeUnknownEffect;
/**
 * @category Decoding
 * @since 4.0.0
 */
export function decodeUnknownPromise(schema) {
  return asPromise(decodeUnknownEffect(schema));
}
/**
 * @category Decoding
 * @since 4.0.0
 */
export function decodePromise(schema) {
  return asPromise(decodeEffect(schema));
}
/**
 * @category Decoding
 * @since 4.0.0
 */
export function decodeUnknownExit(schema) {
  return asExit(decodeUnknownEffect(schema));
}
/**
 * @category Decoding
 * @since 4.0.0
 */
export const decodeExit = decodeUnknownExit;
/**
 * @category Decoding
 * @since 4.0.0
 */
export function decodeUnknownOption(schema) {
  return asOption(decodeUnknownEffect(schema));
}
/**
 * @category Decoding
 * @since 4.0.0
 */
export const decodeOption = decodeUnknownOption;
/**
 * @category Decoding
 * @since 4.0.0
 */
export function decodeUnknownResult(schema) {
  return asResult(decodeUnknownEffect(schema));
}
/**
 * @category Decoding
 * @since 4.0.0
 */
export const decodeResult = decodeUnknownResult;
/**
 * @category Decoding
 * @since 4.0.0
 */
export function decodeUnknownSync(schema) {
  return asSync(decodeUnknownEffect(schema));
}
/**
 * @category Decoding
 * @since 4.0.0
 */
export const decodeSync = decodeUnknownSync;
/**
 * @category Encoding
 * @since 4.0.0
 */
export function encodeUnknownEffect(schema) {
  return run(AST.flip(schema.ast));
}
/**
 * @category Encoding
 * @since 4.0.0
 */
export const encodeEffect = encodeUnknownEffect;
/**
 * @category Encoding
 * @since 4.0.0
 */
export const encodeUnknownPromise = schema => asPromise(encodeUnknownEffect(schema));
/**
 * @category Encoding
 * @since 4.0.0
 */
export const encodePromise = encodeUnknownPromise;
/**
 * @category Encoding
 * @since 4.0.0
 */
export function encodeUnknownExit(schema) {
  return asExit(encodeUnknownEffect(schema));
}
/**
 * @category Encoding
 * @since 4.0.0
 */
export const encodeExit = encodeUnknownExit;
/**
 * @category Encoding
 * @since 4.0.0
 */
export function encodeUnknownOption(schema) {
  return asOption(encodeUnknownEffect(schema));
}
/**
 * @category Encoding
 * @since 4.0.0
 */
export const encodeOption = encodeUnknownOption;
/**
 * @category Encoding
 * @since 4.0.0
 */
export function encodeUnknownResult(schema) {
  return asResult(encodeUnknownEffect(schema));
}
/**
 * @category Encoding
 * @since 4.0.0
 */
export const encodeResult = encodeUnknownResult;
/**
 * @category Encoding
 * @since 4.0.0
 */
export function encodeUnknownSync(schema) {
  return asSync(encodeUnknownEffect(schema));
}
/**
 * @category Encoding
 * @since 4.0.0
 */
export const encodeSync = encodeUnknownSync;
/** @internal */
export function run(ast) {
  const parser = recur(ast);
  return (input, options) => Effect.flatMapEager(parser(Option.some(input), options ?? AST.defaultParseOptions), oa => {
    if (oa._tag === "None") {
      return Effect.fail(new Issue.InvalidValue(oa));
    }
    return Effect.succeed(oa.value);
  });
}
function asPromise(parser) {
  return (input, options) => Effect.runPromise(parser(input, options));
}
function asExit(parser) {
  return (input, options) => Effect.runSyncExit(parser(input, options));
}
/** @internal */
export function asOption(parser) {
  const parserExit = asExit(parser);
  return (input, options) => Exit.getSuccess(parserExit(input, options));
}
function asResult(parser) {
  const parserExit = asExit(parser);
  return (input, options) => {
    const exit = parserExit(input, options);
    if (Exit.isSuccess(exit)) {
      return Result.succeed(exit.value);
    }
    const error = Cause.findError(exit.cause);
    if (Result.isFailure(error)) {
      throw Cause.squash(error.failure);
    }
    return Result.fail(error.success);
  };
}
function asSync(parser) {
  return (input, options) => Effect.runSync(Effect.mapErrorEager(parser(input, options), issue => new Error(issue.toString(), {
    cause: issue
  })));
}
const recur = /*#__PURE__*/memoize(ast => {
  let parser;
  const astOptions = InternalAnnotations.resolve(ast)?.["parseOptions"];
  if (!ast.context && !ast.encoding && !ast.checks) {
    return (ou, options) => {
      parser ??= ast.getParser(recur);
      if (astOptions) {
        options = {
          ...options,
          ...astOptions
        };
      }
      return parser(ou, options);
    };
  }
  const isStructural = AST.isArrays(ast) || AST.isObjects(ast) || AST.isDeclaration(ast) && ast.typeParameters.length > 0;
  return (ou, options) => {
    if (astOptions) {
      options = {
        ...options,
        ...astOptions
      };
    }
    const encoding = ast.encoding;
    let srou;
    if (encoding) {
      const links = encoding;
      const len = links.length;
      for (let i = len - 1; i >= 0; i--) {
        const link = links[i];
        const to = link.to;
        const parser = recur(to);
        srou = srou ? Effect.flatMapEager(srou, ou => parser(ou, options)) : parser(ou, options);
        if (link.transformation._tag === "Transformation") {
          const getter = link.transformation.decode;
          srou = Effect.flatMapEager(srou, ou => getter.run(ou, options));
        } else {
          srou = link.transformation.decode(srou, options);
        }
      }
      srou = Effect.mapErrorEager(srou, issue => new Issue.Encoding(ast, ou, issue));
    }
    parser ??= ast.getParser(recur);
    let sroa = srou ? Effect.flatMapEager(srou, ou => parser(ou, options)) : parser(ou, options);
    if (ast.checks && !options?.disableChecks) {
      const checks = ast.checks;
      if (options?.errors === "all" && isStructural && Option.isSome(ou)) {
        sroa = Effect.catchEager(sroa, issue => {
          const issues = [];
          AST.collectIssues(checks.filter(check => check.annotations?.[AST.STRUCTURAL_ANNOTATION_KEY]), ou.value, issues, ast, options);
          const out = Arr.isArrayNonEmpty(issues) ? issue._tag === "Composite" && issue.ast === ast ? new Issue.Composite(ast, issue.actual, [...issue.issues, ...issues]) : new Issue.Composite(ast, ou, [issue, ...issues]) : issue;
          return Effect.fail(out);
        });
      }
      sroa = Effect.flatMapEager(sroa, oa => {
        if (Option.isSome(oa)) {
          const value = oa.value;
          const issues = [];
          AST.collectIssues(checks, value, issues, ast, options);
          if (Arr.isArrayNonEmpty(issues)) {
            return Effect.fail(new Issue.Composite(ast, oa, issues));
          }
        }
        return Effect.succeed(oa);
      });
    }
    return sroa;
  };
});
//# sourceMappingURL=SchemaParser.js.map