(*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *)

module CamlUnix = Unix
open Core

module Kind = struct
  type t =
    | Watchman
    | BuckInternal
    | BuckUser
    | Pyre
    | Unknown
  [@@deriving sexp, compare, hash, to_yojson]
end

let kind_and_message_from_exception = function
  | Buck.Raw.BuckError { buck_command; arguments; description; exit_code; additional_logs } ->
      (* See fbcode/buck2/app/buck2_client_ctx/src/exit_result.rs *)
      let kind =
        match exit_code with
        | Some 3 -> Kind.BuckUser
        | Some 1 ->
            (* unknown error, treat it as user error to be conservative. *)
            Kind.BuckUser
        | _ -> Kind.BuckInternal
      in
      let reproduce_message =
        if Buck.Raw.ArgumentList.length arguments <= 20 then
          [
            Format.sprintf
              "To reproduce this error, run `%s`."
              (Buck.Raw.ArgumentList.to_buck_command ~buck_command arguments);
          ]
        else
          []
      in
      let additional_messages =
        if List.is_empty additional_logs then
          []
        else
          "Here are the last few lines of Buck log:"
          :: "  ..."
          :: List.map additional_logs ~f:(String.( ^ ) " ")
      in
      ( kind,
        Format.sprintf
          "Cannot build the project: %s.\n%s"
          description
          (String.concat ~sep:"\n" (List.append reproduce_message additional_messages)) )
  | Buck.Interface.JsonError message ->
      ( Kind.Pyre,
        Format.sprintf "Cannot build the project because Buck returns malformed JSON: %s" message )
  | Buck.Builder.LinkTreeConstructionError message ->
      ( Kind.Pyre,
        Format.sprintf
          "Cannot build the project because Pyre encounters a fatal error while constructing a \
           link tree: %s"
          message )
  | ChecksumMap.LoadError message ->
      ( Kind.Pyre,
        Format.sprintf
          "Cannot build the project because Pyre encounters a fatal error while loading external \
           wheel: %s"
          message )
  | Watchman.ConnectionError message ->
      Kind.Watchman, Format.sprintf "Watchman connection error: %s" message
  | Watchman.SubscriptionError message ->
      Kind.Watchman, Format.sprintf "Watchman subscription error: %s" message
  | Watchman.QueryError message -> Kind.Watchman, Format.sprintf "Watchman query error: %s" message
  | CamlUnix.Unix_error (CamlUnix.EADDRINUSE, _, _) ->
      ( Kind.Pyre,
        "A Pyre server is already running for the current project. Use `pyre stop` to stop it \
         before starting another one." )
  | unknown_exn -> Kind.Unknown, Exception.exn_to_string unknown_exn
