{ config, pkgs, lib, ... }:

let
  # pdoc data
  pdoc-builtins = [
    "builtins"
    "os"
    "array"
    "sys"
    "time"
    "traceback"
    "pathlib"
    "itertools"
    "functools"
    "unittest"
    "argparse"
    "asyncio"
    "textwrap"
    "collections"
    "configparser"
    "concurrent"
    "contextlib"
    "operator"
    "pickle" # TODO: marsmellow or whatever
    "copy"
    "ctypes"
    "pprint"
    "shlex"
    "re"
    "abc"
    "ast"
    "random"
    "shutil"
    "sqlite3"
    "subprocess"
    "statistics"
    "string"
    "tarfile"
    "typing"
    "uuid"
    "warnings"
    "wave"
    "dataclasses"
    "glob"
    "gzip"
    "inspect"
    "json"
    "base64"
    "zipfile"
  ];

  pdoc-modules = [
    {name="more-itertools"; literal="more_itertools";}
    "altair"
    "pygal"
    "vispy"
    #"ggplot"
    "seaborn"
    "bokeh"
    "plotly"
    "tabulate"
    "wavefile"
    "moderngl"
    "pydantic"
    "typer"
    "ptpython"
    "colorama"
    {name="pyjwt"; literal="jwt";}
    "zipp"
    "aiofiles"
    "aafigure"
    "urllib3"
    "tesserocr"
    "trio"
    "starlette"
    "pyverilog"
    "nixpkgs"
    "wavedrom"
    "httpx"
    "pyquery"
    "mpv"
    {name="beautifulsoup4"; literal="bs4";}
    "hid"
    #{name="hidapi"; literal="hid";}
    #"sanic" # broken build?
    "paramiko"
    "pydub"
    "aiohttp"
    "papermill"
    "rtoml"
    "redis"
    "numpy"
    #"domeneshop"
    "munch"
    "migen"
    "amaranth"
    "click"
    "attrs"
    "graphviz"
    "baron"
    "redbaron"
    "fastapi"
    "pytest"
    #"pyglet" # pyglet.com fails, windows only
    #"pygame" # pygame.movie fails on pdoc3, pdoc hangs
    "plotly"
    "peewee"
    "parsel"
    "pandas"
    #"mutmut" # moved to toplevel from python3Packages
    "mlflow"
    "meshio"
    #"einops" # depends on tensorflow, which is broken ATM
    "aiodns"
    "json5"
    "seaborn"
    "matplotlib"
    "dash"
    "rarfile"
    "pyramid"
    "pygtail"
    "nbconvert"
    "humanfriendly"
    "pendulum"
    "jsonpickle"
    "cachetools"
    "wrapt"
    "lxml"
    "chardet"
    "yarl"
    "frozenlist"
    "itsdangerous"
    "xmltodict"
    {name="cached-property"; literal="cached_property";}
    "toolz"
    "aioitertools"
    "coconut"
    "asyncpg" #"aiopg"
    {name="libsass"; literal="sass";}
    {name="pytorch"; literal="torch";}
    #{name="pytorch-lightning"; literal="pytorch_lightning";} # broken build?
    {name="pillow"; literal="PIL";}
    "trio"
    "tqdm"
    "rich"
    "pudb"
    "pony"
    "mido"
    "jedi"
    "h5py"
    "atom"
    "toml"
    {name="pyyaml"; literal="yaml";}
    "jinja2"
    "requests"
    "h5py"
    "imageio"
    "pygments"
    "trimesh"
    "shapely"
    #"faiss"
    #"geomloss"
    #"mesh_to_sdf"
    #"pyrender"
  ];
  toName    = x: if builtins.isString x then x else x.name;
  toLiteral = x: if builtins.isString x then x else x.literal;

  mkPdoc = use-pdoc3: isBuiltin: pkg: let
      description = if isBuiltin
        then "builtin"
        else pkgs.python3Packages.${toName pkg}.meta.description;
      version = if isBuiltin
        then "-"
        else pkgs.python3Packages.${toName pkg}.version;
      homepage = if isBuiltin
        then "https://docs.python.org/3/library/${toLiteral pkg}.html"
        else pkgs.python3Packages.${toName pkg}.meta.homepage or "-";
      doc = pkgs.runCommand "pdoc${if use-pdoc3 then "3" else ""}-${toName pkg}-docs" {
        nativeBuildInputs = (if use-pdoc3
          then [pkgs.python3Packages.pdoc3]
          else [pkgs.python3Packages.pdoc])
          ++ lib.optional (!isBuiltin) (builtins.getAttr (toName pkg) pkgs.python3Packages);
        NAME = toName pkg;
        LITERAL = toLiteral pkg;
        # TODO: license
        # TODO: build html with something better than bash
      } ''
        ( timeout 900s ${if !use-pdoc3
          then ''pdoc --no-search --math --no-browser --output-directory $out "$LITERAL"''
          else ''pdoc3 --skip-errors --output-dir $out --html "$LITERAL"''
        } 2>&1 | tee $LITERAL.log ) || true
        mkdir -p $out
        cp $LITERAL.log $out
        test -f $out/index.html && rm -v $out/index.html

        function write {
          { printf "%s" "$@"; echo; } >> $out/index.part-"$LITERAL".html
        }

        write "<tr>"
        if test -f $out/"$LITERAL".html; then
          write "<td><a href=\"$LITERAL.html\">$NAME</a>"
        elif test -d $out/"$LITERAL"; then
          write "<td><a href=\"$LITERAL/\">$NAME</a>"
        else
          write "<td>$NAME"
        fi
        write "<td>${version}"
        if test -s $out/$LITERAL.log; then
          write "<td><a href=\"$LITERAL.log\">log</a>"
        else
          write "<td>-"
        fi
        write "<td>${lib.escapeXML description}"
        ${if homepage == "-" then ''
          write "<td>n/a"
        '' else ''
          write "<td><a href=\"${homepage}\">${homepage}</a>"
        ''}
        write "</tr>"
      '';
      fallback = pkgs.writeTextDir "index.part-${toLiteral pkg}.html" ''
        <tr>
        <td>${toLiteral pkg}
        <td>${version}
        <td>&#10799;
        <td>${lib.escapeXML description}
        <td>${if homepage == "-" then
          "n/a"
         else
          ''<a href="${homepage}">${homepage}</a>''
        }
        </tr>
      '';
    in if (builtins.tryEval doc.outPath).success
      then doc
      else fallback;
  mkPdocs = use-pdoc3: pkgs.symlinkJoin {
    name = "pdoc-docs";
    paths = (map (mkPdoc use-pdoc3 true) pdoc-builtins) ++ (map (mkPdoc use-pdoc3 false) pdoc-modules);
    # note: globs are sorted
    postBuild = ''
      echo "<!DOCTYPE html>"     >> $out/index.html
      echo "<table><tr><th>name<th>version<th>log<th>description<th>homepage</tr>" >> $out/index.html
      cat $out/index.part-*.html >> $out/index.html
      rm $out/index.part-*.html
      echo "</table>"            >> $out/index.html
    '';
  };

in

# don't build if local vm
lib.mkIf (!config.virtualisation.isVmVariant)

{

  # Pdoc
  # Auto-generate API documentation for Python projects.

  services.docs-to-host.docs = [
    {
      dirname = "pdoc-docs";
      path    = mkPdocs false;
      desc    = "Documentation for various python modules, generated with pdoc";
    }
    {
      dirname = "pdoc3-docs";
      path    = mkPdocs true;
      desc    = "Documentation for various python modules, generated with pdoc3";
    }
  ];


  /** /
  services.nginx.virtualHosts.${mkDomain "pdoc"} = {
    forceSSL = true; # addSSL = true;
    enableACME = true; #useACMEHost = acmeDomain;
    root = mkPdocs false;
  };
  services.nginx.virtualHosts.${mkDomain "pdoc3"} = {
    forceSSL = true; # addSSL = true;
    enableACME = true; #useACMEHost = acmeDomain;
    root = mkPdocs true;
  };
  /**/

}
