#use "topfind"

#thread

#require "yojson"

#require "ben"

#require "ben.frontends"

open Ben

let trigger = Sys.argv.(1)

let sources_bdeps, bin_map =
  Ben_frontends.Utils.parse_control_file Source
    "repo/dists/rebuilt/main/source/Sources"
    (fun _ -> true)
    (fun p x (accu, bin_map) ->
      let bin_map =
        Package.binaries x
        |> List.fold_left (fun accu b -> Package.Map.add b p accu) bin_map
      in
      let accu =
        if Package.has "testsuite" x then Package.Map.add p x accu else accu
      in
      (accu, bin_map))
    (Package.Map.empty, Package.Map.empty)

let split =
  let rex = Re.Pcre.regexp "\\s*,\\s*" in
  fun x -> Re.split rex x

let to_set x =
  List.fold_left (fun accu x -> Package.Set.add x accu) Package.Set.empty x

let sources_triggers =
  Ben_frontends.Utils.parse_control_file Source "ben/Sources"
    (fun _ -> true)
    (fun p x accu ->
      match Package.get "testsuite-triggers" x with
      | exception Not_found -> accu
      | triggers ->
          let triggers =
            split triggers
            |> List.map (function
              | "@builddeps@" -> Package.build_depends x
              | x -> [ Package.Name.of_string x ])
            |> List.flatten
            |> List.filter_map (fun d -> Package.Map.find_opt d bin_map)
            |> to_set
          in
          if Package.Set.is_empty triggers then accu
          else Package.Map.add p triggers accu)
    Package.Map.empty

let sources_bdeps =
  sources_bdeps
  |> Package.Map.mapi (fun _ x ->
      Package.build_depends x
      |> List.filter_map (fun d -> Package.Map.find_opt d bin_map)
      |> to_set)

let union a b = Package.Set.fold Package.Set.add a b

let sources =
  Package.Map.fold
    (fun p d accu ->
      match Package.Map.find_opt p accu with
      | None -> Package.Map.add p d accu
      | Some d' -> Package.Map.add p (union d d') accu)
    sources_bdeps sources_triggers

let tests : Yojson.Safe.t =
  Package.Map.bindings sources
  |> List.map (fun (p, d) ->
      let package = Package.Name.to_string p in
      let pin_packages =
        Package.Set.add p d |> Package.Set.elements
        |> List.map (fun x -> "src:" ^ Package.Name.to_string x)
        |> String.concat ","
        |> fun x -> `List [ `List [ `String x; `String "unstable-ocaml" ] ]
      in
      `Assoc
        [
          ("trigger", `String trigger);
          ("package", `String package);
          ("pin-packages", pin_packages);
          ("extra-apt-sources", `List [ `String "ocaml-next" ]);
        ])
  |> fun x -> `List x

let test_requests : Yojson.Safe.t =
  `List
    [
      `Assoc
        [
          ("arch", `List [ `String "amd64" ]);
          ("suite", `String "unstable");
          ("tests", tests);
        ];
    ]

let () = print_string (Yojson.Safe.to_string test_requests)
