(* Copyright © 2026 Stéphane Glondu. Licensed under GPL-2+. *)

open Support
open Common

let is_deb_dev x = extract ~prefix:"lib" ~suffix:"-ocaml-dev" x
let is_deb_run x = extract ~prefix:"lib" ~suffix:"-ocaml" x
let is_stub x = extract ~prefix:"lib" ~suffix:".a" (Filename.basename x)

let is_run_file x =
  let suffixes = [ "/META"; ".cma"; ".cmxs" ] in
  List.exists (fun suffix -> String.ends_with ~suffix x) suffixes

module Make () : sig end = struct
  let () =
    match Lazy.force config with
    | Some (`Assoc o) -> (
        match List.assoc_opt "dh_ocamllibinstall" o with
        | Some (`Bool true) -> ()
        | _ -> exit 0)
    | _ -> exit 0

  let deb_packages = "dh_listpackages" |> pread |> split_lines_opt
  let deb_dev_packages = List.filter_map is_deb_dev deb_packages
  let deb_run_packages = List.filter_map is_deb_run deb_packages
  let main_lib = match deb_dev_packages with [] -> exit 0 | x :: _ -> x

  let findlib_packages =
    let list =
      match loadFile "debian/debian-dune" with
      | Some _ as x -> x
      | None -> loadFile "debian/debian-findlib"
    in
    split_lines_opt list
    |> List.map (fun x ->
        match String.split_on_char ' ' x with
        | [ x ] -> (x, main_lib)
        | [ x; y ] -> (x, y)
        | _ -> exit 11)

  let files =
    let prefix = Printf.sprintf "debian/tmp%s" (Lazy.force ocaml_stdlib_dir) in
    let n = String.length prefix + 1 in
    Printf.sprintf "find %s -type f" prefix
    |> pread |> split_lines_opt
    |> List.map (fun x -> String.sub x n (String.length x - n))

  let process_findlib_package (files, accu) (findlib, deb) =
    let my, files =
      List.partition (String.starts_with ~prefix:(findlib ^ "/")) files
    in
    let stubs = List.filter_map is_stub my in
    let dlls, files =
      List.fold_left
        (fun (dlls, files) stub ->
          let dll = Printf.sprintf "stublibs/dll%s.so" stub in
          match mem_remove dll files with
          | None -> (dlls, files)
          | Some files -> (dll :: dlls, files))
        ([], files) stubs
    in
    if List.mem deb deb_run_packages then
      let run_files, dev_files = List.partition is_run_file my in
      let accu =
        let pkg = Printf.sprintf "lib%s-ocaml" deb in
        let current = SMap.find_opt pkg accu |> Option.value ~default:[] in
        let current = List.rev_append run_files current in
        let current = List.rev_append dlls current in
        SMap.add pkg current accu
      in
      let accu =
        let pkg = Printf.sprintf "lib%s-ocaml-dev" deb in
        let current = SMap.find_opt pkg accu |> Option.value ~default:[] in
        let current = List.rev_append dev_files current in
        SMap.add pkg current accu
      in
      (files, accu)
    else
      let pkg = Printf.sprintf "lib%s-ocaml-dev" deb in
      let current = SMap.find_opt pkg accu |> Option.value ~default:[] in
      let current = List.rev_append my current in
      let current = List.rev_append dlls current in
      (files, SMap.add pkg current accu)

  let _, map =
    List.fold_left process_findlib_package (files, SMap.empty) findlib_packages

  let () =
    SMap.iter
      (fun pkg files ->
        let aux_file =
          Printf.sprintf "debian/%s.ocamllibinstall.debhelper" pkg
        in
        let () =
          files
          |> List.map (Printf.sprintf "%s/%s" (Lazy.force ocaml_stdlib_dir))
          |> merge_lines |> saveFile aux_file
        in
        let cmd =
          Printf.sprintf "xargs dh_install -p%s --autodest < %s" pkg aux_file
        in
        match pread cmd with
        | None ->
            printf "E: could not run %S\n" cmd;
            exit 12
        | Some "" -> printf "I: %S produced no output\n" cmd
        | Some x -> printf "W: %S produced output:\n%s" cmd x)
      map
end

let main _ =
  let module _ = Make () in
  ()
