(*
 * 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.
 *)

open File_sig

(* Collect the names and locations of types that are available as we scan
 * the imports. Later we'll match them with some remote defining loc. *)
type acc_t = Ty.imported_ident list

let from_imported_locs_map ~import_mode map (acc : acc_t) =
  SMap.fold
    (fun _remote remote_map acc ->
      SMap.fold
        (fun local imported_locs_nel acc ->
          Nel.fold_left
            (fun acc { local_loc; _ } -> (ALoc.of_loc local_loc, local, import_mode) :: acc)
            acc
            imported_locs_nel)
        remote_map
        acc)
    map
    acc

let rec from_binding ~import_mode binding (acc : acc_t) =
  match binding with
  | BindIdent (loc, name) -> (ALoc.of_loc loc, name, import_mode) :: acc
  | BindNamed map ->
    List.fold_left (fun acc (_, binding) -> from_binding ~import_mode binding acc) acc map

let from_bindings ~import_mode bindings_opt acc =
  match bindings_opt with
  | Some bindings -> from_binding ~import_mode bindings acc
  | None -> acc

let from_require require (acc : acc_t) =
  match require with
  | Require { source = _; require_loc = _; bindings; prefix = _ } ->
    from_bindings ~import_mode:Ty.ValueMode bindings acc
  | Import { import_loc = _; source = _; named; ns = _; types; typesof; typesof_ns = _ } ->
    (* TODO import namespaces (`ns`) as modules that might contain imported types *)
    acc
    |> from_imported_locs_map ~import_mode:Ty.ValueMode named
    |> from_imported_locs_map ~import_mode:Ty.TypeMode types
    |> from_imported_locs_map ~import_mode:Ty.TypeofMode typesof
  | ImportDynamic _
  | Import0 _
  | ImportSynthetic _
  | ExportFrom _ ->
    acc

let extract_imported_idents file_sig =
  List.fold_left (fun acc require -> from_require require acc) [] (File_sig.requires file_sig)

let extract_schemes cx file_sig typed_ast =
  file_sig
  |> File_sig.requires
  |> List.fold_left (fun acc require -> from_require require acc) []
  |> List.fold_left
       (fun acc (loc, name, import_mode) ->
         match Typed_ast_finder.find_exact_match_annotation cx typed_ast loc with
         | Some scheme -> (name, loc, import_mode, scheme) :: acc
         | None -> acc)
       []
