import {
  Ok,
  Error,
  toList,
  Empty as $Empty,
  prepend as listPrepend,
  CustomType as $CustomType,
  isEqual,
} from "../../gleam.mjs";
import * as $bit_array from "../../gleam/bit_array.mjs";
import * as $dict from "../../gleam/dict.mjs";
import * as $dynamic from "../../gleam/dynamic.mjs";
import * as $int from "../../gleam/int.mjs";
import * as $list from "../../gleam/list.mjs";
import * as $option from "../../gleam/option.mjs";
import { None, Some } from "../../gleam/option.mjs";
import {
  index as bare_index,
  int as dynamic_int,
  float as dynamic_float,
  bit_array as dynamic_bit_array,
  list as decode_list,
  dict as decode_dict,
  identity as cast,
  is_null,
  string as dynamic_string,
} from "../../gleam_stdlib.mjs";

export class DecodeError extends $CustomType {
  constructor(expected, found, path) {
    super();
    this.expected = expected;
    this.found = found;
    this.path = path;
  }
}
export const DecodeError$DecodeError = (expected, found, path) =>
  new DecodeError(expected, found, path);
export const DecodeError$isDecodeError = (value) =>
  value instanceof DecodeError;
export const DecodeError$DecodeError$expected = (value) => value.expected;
export const DecodeError$DecodeError$0 = (value) => value.expected;
export const DecodeError$DecodeError$found = (value) => value.found;
export const DecodeError$DecodeError$1 = (value) => value.found;
export const DecodeError$DecodeError$path = (value) => value.path;
export const DecodeError$DecodeError$2 = (value) => value.path;

class Decoder extends $CustomType {
  constructor(function$) {
    super();
    this.function = function$;
  }
}

/**
 * Run a decoder on a `Dynamic` value, decoding the value if it is of the
 * desired type, or returning errors.
 *
 * # Examples
 *
 * ```gleam
 * let decoder = {
 *   use name <- decode.field("email", decode.string)
 *   use email <- decode.field("password", decode.string)
 *   decode.success(SignUp(name: name, email: email))
 * }
 *
 * decode.run(data, decoder)
 * ```
 */
export function run(data, decoder) {
  let $ = decoder.function(data);
  let maybe_invalid_data;
  let errors;
  maybe_invalid_data = $[0];
  errors = $[1];
  if (errors instanceof $Empty) {
    return new Ok(maybe_invalid_data);
  } else {
    return new Error(errors);
  }
}

/**
 * Finalise a decoder having successfully extracted a value.
 *
 * # Examples
 *
 * ```gleam
 * let data = dynamic.properties([
 *   #(dynamic.string("email"), dynamic.string("lucy@example.com")),
 *   #(dynamic.string("name"), dynamic.string("Lucy")),
 * ]))
 *
 * let decoder = {
 *   use name <- decode.field("name", string)
 *   use email <- decode.field("email", string)
 *   decode.success(SignUp(name: name, email: email))
 * }
 *
 * let result = decode.run(data, decoder)
 * assert result == Ok(SignUp(name: "Lucy", email: "lucy@example.com"))
 * ```
 */
export function success(data) {
  return new Decoder((_) => { return [data, toList([])]; });
}

function decode_dynamic(data) {
  return [data, toList([])];
}

/**
 * Apply a transformation function to any value decoded by the decoder.
 *
 * # Examples
 *
 * ```gleam
 * let decoder = decode.int |> decode.map(int.to_string)
 * let result = decode.run(dynamic.int(1000), decoder)
 * assert result == Ok("1000")
 * ```
 */
export function map(decoder, transformer) {
  return new Decoder(
    (d) => {
      let $ = decoder.function(d);
      let data;
      let errors;
      data = $[0];
      errors = $[1];
      return [transformer(data), errors];
    },
  );
}

/**
 * Apply a transformation function to any errors returned by the decoder.
 */
export function map_errors(decoder, transformer) {
  return new Decoder(
    (d) => {
      let $ = decoder.function(d);
      let data;
      let errors;
      data = $[0];
      errors = $[1];
      return [data, transformer(errors)];
    },
  );
}

/**
 * Create a new decoder based upon the value of a previous decoder.
 *
 * This may be useful to run one previous decoder to use in further decoding.
 */
export function then$(decoder, next) {
  return new Decoder(
    (dynamic_data) => {
      let $ = decoder.function(dynamic_data);
      let data;
      let errors;
      data = $[0];
      errors = $[1];
      let decoder$1 = next(data);
      let $1 = decoder$1.function(dynamic_data);
      let layer;
      let data$1;
      layer = $1;
      data$1 = $1[0];
      if (errors instanceof $Empty) {
        return layer;
      } else {
        return [data$1, errors];
      }
    },
  );
}

function run_decoders(loop$data, loop$failure, loop$decoders) {
  while (true) {
    let data = loop$data;
    let failure = loop$failure;
    let decoders = loop$decoders;
    if (decoders instanceof $Empty) {
      return failure;
    } else {
      let decoder = decoders.head;
      let decoders$1 = decoders.tail;
      let $ = decoder.function(data);
      let layer;
      let errors;
      layer = $;
      errors = $[1];
      if (errors instanceof $Empty) {
        return layer;
      } else {
        loop$data = data;
        loop$failure = failure;
        loop$decoders = decoders$1;
      }
    }
  }
}

/**
 * Create a new decoder from several other decoders. Each of the inner
 * decoders is run in turn, and the value from the first to succeed is used.
 *
 * If no decoder succeeds then the errors from the first decoder is used.
 * If you wish for different errors then you may wish to use the
 * `collapse_errors` or `map_errors` functions.
 *
 * # Examples
 *
 * ```gleam
 * let decoder = decode.one_of(decode.string, or: [
 *   decode.int |> decode.map(int.to_string),
 *   decode.float |> decode.map(float.to_string),
 * ])
 * decode.run(dynamic.int(1000), decoder)
 * // -> Ok("1000")
 * ```
 */
export function one_of(first, alternatives) {
  return new Decoder(
    (dynamic_data) => {
      let $ = first.function(dynamic_data);
      let layer;
      let errors;
      layer = $;
      errors = $[1];
      if (errors instanceof $Empty) {
        return layer;
      } else {
        return run_decoders(dynamic_data, layer, alternatives);
      }
    },
  );
}

/**
 * Create a decoder that can refer to itself, useful for decoding deeply
 * nested data.
 *
 * Attempting to create a recursive decoder without this function could result
 * in an infinite loop. If you are using `field` or other `use`able functions
 * then you may not need to use this function.
 *
 * ```gleam
 * type Nested {
 *   Nested(List(Nested))
 *   Value(String)
 * }
 *
 * fn nested_decoder() -> decode.Decoder(Nested) {
 *   use <- decode.recursive
 *   decode.one_of(decode.string |> decode.map(Value), [
 *     decode.list(nested_decoder()) |> decode.map(Nested),
 *   ])
 * }
 * ```
 */
export function recursive(inner) {
  return new Decoder(
    (data) => {
      let decoder = inner();
      return decoder.function(data);
    },
  );
}

/**
 * A decoder that decodes nullable values of a type decoded by with a given
 * decoder.
 *
 * This function can handle common representations of null on all runtimes, such as
 * `nil`, `null`, and `undefined` on Erlang, and `undefined` and `null` on
 * JavaScript.
 *
 * # Examples
 *
 * ```gleam
 * let result = decode.run(dynamic.int(100), decode.optional(decode.int))
 * assert result == Ok(option.Some(100))
 * ```
 *
 * ```gleam
 * let result = decode.run(dynamic.nil(), decode.optional(decode.int))
 * assert result == Ok(option.None)
 * ```
 */
export function optional(inner) {
  return new Decoder(
    (data) => {
      let $ = is_null(data);
      if ($) {
        return [new $option.None(), toList([])];
      } else {
        let $1 = inner.function(data);
        let data$1;
        let errors;
        data$1 = $1[0];
        errors = $1[1];
        return [new $option.Some(data$1), errors];
      }
    },
  );
}

/**
 * A decoder that decodes `Dynamic` values. This decoder never returns an error.
 *
 * # Examples
 *
 * ```gleam
 * let result = decode.run(dynamic.float(3.14), decode.dynamic)
 * assert result == Ok(dynamic.float(3.14))
 * ```
 */
export const dynamic = /* @__PURE__ */ new Decoder(decode_dynamic);

/**
 * Construct a decode error for some unexpected dynamic data.
 */
export function decode_error(expected, found) {
  return toList([
    new DecodeError(expected, $dynamic.classify(found), toList([])),
  ]);
}

function run_dynamic_function(data, name, f) {
  let $ = f(data);
  if ($ instanceof Ok) {
    let data$1 = $[0];
    return [data$1, toList([])];
  } else {
    let zero = $[0];
    return [
      zero,
      toList([new DecodeError(name, $dynamic.classify(data), toList([]))]),
    ];
  }
}

function decode_bool(data) {
  let $ = isEqual(cast(true), data);
  if ($) {
    return [true, toList([])];
  } else {
    let $1 = isEqual(cast(false), data);
    if ($1) {
      return [false, toList([])];
    } else {
      return [false, decode_error("Bool", data)];
    }
  }
}

function decode_int(data) {
  return run_dynamic_function(data, "Int", dynamic_int);
}

function decode_float(data) {
  return run_dynamic_function(data, "Float", dynamic_float);
}

function decode_bit_array(data) {
  return run_dynamic_function(data, "BitArray", dynamic_bit_array);
}

/**
 * Replace all errors produced by a decoder with one single error for a named
 * expected type.
 *
 * This function may be useful if you wish to simplify errors before
 * presenting them to a user, particularly when using the `one_of` function.
 *
 * # Examples
 *
 * ```gleam
 * let decoder = decode.string |> decode.collapse_errors("MyThing")
 * let result = decode.run(dynamic.int(1000), decoder)
 * assert result == Error([DecodeError("MyThing", "Int", [])])
 * ```
 */
export function collapse_errors(decoder, name) {
  return new Decoder(
    (dynamic_data) => {
      let $ = decoder.function(dynamic_data);
      let layer;
      let data;
      let errors;
      layer = $;
      data = $[0];
      errors = $[1];
      if (errors instanceof $Empty) {
        return layer;
      } else {
        return [data, decode_error(name, dynamic_data)];
      }
    },
  );
}

/**
 * Define a decoder that always fails. The parameter for this function is the
 * name of the type that has failed to decode.
 */
export function failure(zero, expected) {
  return new Decoder((d) => { return [zero, decode_error(expected, d)]; });
}

/**
 * Create a decoder for a new data type from a decoding function.
 *
 * This function is used for new primitive types. For example, you might
 * define a decoder for Erlang's pid type.
 *
 * A default "zero" value is also required to make a decoder. When this
 * decoder is used as part of a larger decoder this zero value used as
 * a placeholder so that the rest of the decoder can continue to run and
 * collect all decoding errors.
 *
 * If you were to make a decoder for the `String` type (rather than using the
 * build-in `string` decoder) you would define it like so:
 *
 * ```gleam
 * pub fn string_decoder() -> decode.Decoder(String) {
 *   let default = ""
 *   decode.new_primitive_decoder("String", fn(data) {
 *     case dynamic.string(data) {
 *       Ok(x) -> Ok(x)
 *       Error(_) -> Error(default)
 *     }
 *   })
 * }
 * ```
 */
export function new_primitive_decoder(name, decoding_function) {
  return new Decoder(
    (d) => {
      let $ = decoding_function(d);
      if ($ instanceof Ok) {
        let t = $[0];
        return [t, toList([])];
      } else {
        let zero = $[0];
        return [
          zero,
          toList([new DecodeError(name, $dynamic.classify(d), toList([]))]),
        ];
      }
    },
  );
}

/**
 * A decoder that decodes `Bool` values.
 *
 * # Examples
 *
 * ```gleam
 * let result = decode.run(dynamic.bool(True), decode.bool)
 * assert result == Ok(True)
 * ```
 */
export const bool = /* @__PURE__ */ new Decoder(decode_bool);

/**
 * A decoder that decodes `Int` values.
 *
 * # Examples
 *
 * ```gleam
 * let result = decode.run(dynamic.int(147), decode.int)
 * assert result == Ok(147)
 * ```
 */
export const int = /* @__PURE__ */ new Decoder(decode_int);

/**
 * A decoder that decodes `Float` values.
 *
 * # Examples
 *
 * ```gleam
 * let result = decode.run(dynamic.float(3.14), decode.float)
 * assert result == Ok(3.14)
 * ```
 */
export const float = /* @__PURE__ */ new Decoder(decode_float);

/**
 * A decoder that decodes `BitArray` values. This decoder never returns an error.
 *
 * # Examples
 *
 * ```gleam
 * let result = decode.run(dynamic.bit_array(<<5, 7>>), decode.bit_array)
 * assert result == Ok(<<5, 7>>)
 * ```
 */
export const bit_array = /* @__PURE__ */ new Decoder(decode_bit_array);

function decode_string(data) {
  return run_dynamic_function(data, "String", dynamic_string);
}

/**
 * A decoder that decodes `String` values.
 *
 * # Examples
 *
 * ```gleam
 * let result = decode.run(dynamic.string("Hello!"), decode.string)
 * assert result == Ok("Hello!")
 * ```
 */
export const string = /* @__PURE__ */ new Decoder(decode_string);

function fold_dict(acc, key, value, key_decoder, value_decoder) {
  let $ = key_decoder(key);
  let $1 = $[1];
  if ($1 instanceof $Empty) {
    let key$1 = $[0];
    let $2 = value_decoder(value);
    let $3 = $2[1];
    if ($3 instanceof $Empty) {
      let value$1 = $2[0];
      let dict$1 = $dict.insert(acc[0], key$1, value$1);
      return [dict$1, acc[1]];
    } else {
      let errors = $3;
      return push_path([$dict.new$(), errors], toList(["values"]));
    }
  } else {
    let errors = $1;
    return push_path([$dict.new$(), errors], toList(["keys"]));
  }
}

/**
 * A decoder that decodes dicts where all keys and vales are decoded with
 * given decoders.
 *
 * # Examples
 *
 * ```gleam
 * let values = dynamic.properties([
 *   #(dynamic.string("one"), dynamic.int(1)),
 *   #(dynamic.string("two"), dynamic.int(2)),
 * ])
 *
 * let result =
 *   decode.run(values, decode.dict(decode.string, decode.int))
 * assert result == Ok(values)
 * ```
 */
export function dict(key, value) {
  return new Decoder(
    (data) => {
      let $ = decode_dict(data);
      if ($ instanceof Ok) {
        let dict$1 = $[0];
        return $dict.fold(
          dict$1,
          [$dict.new$(), toList([])],
          (a, k, v) => {
            let $1 = a[1];
            if ($1 instanceof $Empty) {
              return fold_dict(a, k, v, key.function, value.function);
            } else {
              return a;
            }
          },
        );
      } else {
        return [$dict.new$(), decode_error("Dict", data)];
      }
    },
  );
}

/**
 * A decoder that decodes lists where all elements are decoded with a given
 * decoder.
 *
 * # Examples
 *
 * ```gleam
 * let result =
 *   [1, 2, 3]
 *   |> list.map(dynamic.int)
 *   |> dynamic.list
 *   |> decode.run(decode.list(of: decode.int))
 * assert result == Ok([1, 2, 3])
 * ```
 */
export function list(inner) {
  return new Decoder(
    (data) => {
      return decode_list(
        data,
        inner.function,
        (p, k) => { return push_path(p, toList([k])); },
        0,
        toList([]),
      );
    },
  );
}

function push_path(layer, path) {
  let decoder = one_of(
    string,
    toList([
      (() => {
        let _pipe = int;
        return map(_pipe, $int.to_string);
      })(),
    ]),
  );
  let path$1 = $list.map(
    path,
    (key) => {
      let key$1 = cast(key);
      let $ = run(key$1, decoder);
      if ($ instanceof Ok) {
        let key$2 = $[0];
        return key$2;
      } else {
        return ("<" + $dynamic.classify(key$1)) + ">";
      }
    },
  );
  let errors = $list.map(
    layer[1],
    (error) => {
      return new DecodeError(
        error.expected,
        error.found,
        $list.append(path$1, error.path),
      );
    },
  );
  return [layer[0], errors];
}

function index(
  loop$path,
  loop$position,
  loop$inner,
  loop$data,
  loop$handle_miss
) {
  while (true) {
    let path = loop$path;
    let position = loop$position;
    let inner = loop$inner;
    let data = loop$data;
    let handle_miss = loop$handle_miss;
    if (path instanceof $Empty) {
      let _pipe = data;
      let _pipe$1 = inner(_pipe);
      return push_path(_pipe$1, $list.reverse(position));
    } else {
      let key = path.head;
      let path$1 = path.tail;
      let $ = bare_index(data, key);
      if ($ instanceof Ok) {
        let $1 = $[0];
        if ($1 instanceof Some) {
          let data$1 = $1[0];
          loop$path = path$1;
          loop$position = listPrepend(key, position);
          loop$inner = inner;
          loop$data = data$1;
          loop$handle_miss = handle_miss;
        } else {
          return handle_miss(data, listPrepend(key, position));
        }
      } else {
        let kind = $[0];
        let $1 = inner(data);
        let default$;
        default$ = $1[0];
        let _pipe = [
          default$,
          toList([new DecodeError(kind, $dynamic.classify(data), toList([]))]),
        ];
        return push_path(_pipe, $list.reverse(position));
      }
    }
  }
}

/**
 * The same as [`field`](#field), except taking a path to the value rather
 * than a field name.
 *
 * This function will index into dictionaries with any key type, and if the key is
 * an int then it'll also index into Erlang tuples and JavaScript arrays, and
 * the first eight elements of Gleam lists.
 *
 * # Examples
 *
 * ```gleam
 * let data = dynamic.properties([
 *   #(dynamic.string("data"), dynamic.properties([
 *     #(dynamic.string("email"), dynamic.string("lucy@example.com")),
 *     #(dynamic.string("name"), dynamic.string("Lucy")),
 *   ])
 * ]))
 *
 * let decoder = {
 *   use name <- decode.subfield(["data", "name"], decode.string)
 *   use email <- decode.subfield(["data", "email"], decode.string)
 *   decode.success(SignUp(name: name, email: email))
 * }
 * let result = decode.run(data, decoder)
 * assert result == Ok(SignUp(name: "Lucy", email: "lucy@example.com"))
 * ```
 */
export function subfield(field_path, field_decoder, next) {
  return new Decoder(
    (data) => {
      let $ = index(
        field_path,
        toList([]),
        field_decoder.function,
        data,
        (data, position) => {
          let $1 = field_decoder.function(data);
          let default$;
          default$ = $1[0];
          let _pipe = [
            default$,
            toList([new DecodeError("Field", "Nothing", toList([]))]),
          ];
          return push_path(_pipe, $list.reverse(position));
        },
      );
      let out;
      let errors1;
      out = $[0];
      errors1 = $[1];
      let $1 = next(out).function(data);
      let out$1;
      let errors2;
      out$1 = $1[0];
      errors2 = $1[1];
      return [out$1, $list.append(errors1, errors2)];
    },
  );
}

/**
 * A decoder that decodes a value that is nested within other values. For
 * example, decoding a value that is within some deeply nested JSON objects.
 *
 * This function will index into dictionaries with any key type, and if the key is
 * an int then it'll also index into Erlang tuples and JavaScript arrays, and
 * the first eight elements of Gleam lists.
 *
 * # Examples
 *
 * ```gleam
 * let decoder = decode.at(["one", "two"], decode.int)
 *
 * let data = dynamic.properties([
 *   #(dynamic.string("one"), dynamic.properties([
 *     #(dynamic.string("two"), dynamic.int(1000)),
 *   ])),
 * ]))
 *
 *
 * decode.run(data, decoder)
 * // -> Ok(1000)
 * ```
 *
 * ```gleam
 * dynamic.nil()
 * |> decode.run(decode.optional(decode.int))
 * // -> Ok(option.None)
 * ```
 */
export function at(path, inner) {
  return new Decoder(
    (data) => {
      return index(
        path,
        toList([]),
        inner.function,
        data,
        (data, position) => {
          let $ = inner.function(data);
          let default$;
          default$ = $[0];
          let _pipe = [
            default$,
            toList([new DecodeError("Field", "Nothing", toList([]))]),
          ];
          return push_path(_pipe, $list.reverse(position));
        },
      );
    },
  );
}

/**
 * Run a decoder on a field of a `Dynamic` value, decoding the value if it is
 * of the desired type, or returning errors. An error is returned if there is
 * no field for the specified key.
 *
 * This function will index into dictionaries with any key type, and if the key is
 * an int then it'll also index into Erlang tuples and JavaScript arrays, and
 * the first eight elements of Gleam lists.
 *
 * # Examples
 *
 * ```gleam
 * let data = dynamic.properties([
 *   #(dynamic.string("email"), dynamic.string("lucy@example.com")),
 *   #(dynamic.string("name"), dynamic.string("Lucy")),
 * ]))
 *
 * let decoder = {
 *   use name <- decode.field("name", string)
 *   use email <- decode.field("email", string)
 *   decode.success(SignUp(name: name, email: email))
 * }
 *
 * let result = decode.run(data, decoder)
 * assert result == Ok(SignUp(name: "Lucy", email: "lucy@example.com"))
 * ```
 *
 * If you wish to decode a value that is more deeply nested within the dynamic
 * data, see [`subfield`](#subfield) and [`at`](#at).
 *
 * If you wish to return a default in the event that a field is not present,
 * see [`optional_field`](#optional_field) and / [`optionally_at`](#optionally_at).
 */
export function field(field_name, field_decoder, next) {
  return subfield(toList([field_name]), field_decoder, next);
}

/**
 * Run a decoder on a field of a `Dynamic` value, decoding the value if it is
 * of the desired type, or returning errors. The given default value is
 * returned if there is no field for the specified key.
 *
 * This function will index into dictionaries with any key type, and if the key is
 * an int then it'll also index into Erlang tuples and JavaScript arrays, and
 * the first eight elements of Gleam lists.
 *
 * # Examples
 *
 * ```gleam
 * let data = dynamic.properties([
 *   #(dynamic.string("name"), dynamic.string("Lucy")),
 * ]))
 *
 * let decoder = {
 *   use name <- decode.field("name", string)
 *   use email <- decode.optional_field("email", "n/a", string)
 *   decode.success(SignUp(name: name, email: email))
 * }
 *
 * let result = decode.run(data, decoder)
 * assert result == Ok(SignUp(name: "Lucy", email: "n/a"))
 * ```
 */
export function optional_field(key, default$, field_decoder, next) {
  return new Decoder(
    (data) => {
      let _block;
      let _block$1;
      let $1 = bare_index(data, key);
      if ($1 instanceof Ok) {
        let $2 = $1[0];
        if ($2 instanceof Some) {
          let data$1 = $2[0];
          _block$1 = field_decoder.function(data$1);
        } else {
          _block$1 = [default$, toList([])];
        }
      } else {
        let kind = $1[0];
        _block$1 = [
          default$,
          toList([new DecodeError(kind, $dynamic.classify(data), toList([]))]),
        ];
      }
      let _pipe = _block$1;
      _block = push_path(_pipe, toList([key]));
      let $ = _block;
      let out;
      let errors1;
      out = $[0];
      errors1 = $[1];
      let $2 = next(out).function(data);
      let out$1;
      let errors2;
      out$1 = $2[0];
      errors2 = $2[1];
      return [out$1, $list.append(errors1, errors2)];
    },
  );
}

/**
 * A decoder that decodes a value that is nested within other values. For
 * example, decoding a value that is within some deeply nested JSON objects.
 *
 * This function will index into dictionaries with any key type, and if the key is
 * an int then it'll also index into Erlang tuples and JavaScript arrays, and
 * the first eight elements of Gleam lists.
 *
 * # Examples
 *
 * ```gleam
 * let decoder = decode.optionally_at(["one", "two"], 100, decode.int)
 *
 * let data = dynamic.properties([
 *   #(dynamic.string("one"), dynamic.properties([])),
 * ]))
 *
 *
 * decode.run(data, decoder)
 * // -> Ok(100)
 * ```
 */
export function optionally_at(path, default$, inner) {
  return new Decoder(
    (data) => {
      return index(
        path,
        toList([]),
        inner.function,
        data,
        (_, _1) => { return [default$, toList([])]; },
      );
    },
  );
}
