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

let
  cfg = config.programs.mpv;
  opts = options.programs.mpv;

  toMpvIniString = attrset: lib.pipe attrset [
    (lib.mapAttrsToList (name: value: "${name}=${value}"))
    (lib.concatStringsSep "\n")
  ];

  configDir = pkgs.symlinkJoin {
    name = "mpv-config-dir";
    paths = lib.optional opts.settings.mpv.isDefined (pkgs.writeTextFile {
      name = "mpv-config-dir-mpv.conf";
      destination = "/share/mpv/mpv.conf";
      text = toMpvIniString cfg.settings.mpv;
    }) ++ lib.optional opts.settings.input.isDefined (pkgs.writeTextFile {
      name = "mpv-config-dir-input.conf";
      destination = "/share/mpv/input.conf";
      text = cfg.settings.input;
    }) ++ lib.mapAttrsToList (filename: opts: pkgs.writeTextFile {
      name = "mpv-config-dir-script-opts-${filename}";
      destination = "/share/mpv/script-opts/${filename}";
      text = toMpvIniString opts;
    }) cfg.settings.script-opts;
  };

  wrappedMpv = cfg.package.wrapper {
    mpv = cfg.package;
    youtubeSupport = cfg.youtubeSupport;
    scripts = cfg.scripts;
    extraMakeWrapperArgs = lib.optionals (lib.any (x: x) [
      opts.settings.mpv.isDefined
      opts.settings.input.isDefined
      (lib.length (lib.attrNames cfg.settings.script-opts) > 0)
    ]) [
      "--add-flags" "--config-dir='${configDir}/share/mpv'"
    ];
  };
in
{
  options.programs.mpv = {
    enable = lib.mkEnableOption "mpv";
    package = lib.mkPackageOption pkgs "mpv-unwrapped" {};
    scripts = lib.mkOption {
      type = lib.types.listOf lib.types.package;
      default = [];
    };
    youtubeSupport = lib.mkEnableOption "yt-dlp support for mpv" // {
      default = true;
    };
    settings = let
      mpvini = lib.types.attrsOf lib.types.str;
    in {
      script-opts = lib.mkOption {
        type = lib.types.attrsOf mpvini;
        default = {};
        example = {
          "crop.conf".draw_crosshair = "yes";
        };
        description = ''
          A map of script options for mpv scripts.
          The key is the filename of the script, and the value is a map of options.
        '';
      };
      input = lib.mkOption {
        type = lib.types.separatedString "\n";
        example = ''
          Alt+1 set window-scale 0.125
        '';
        description = ''
          A list of input commands to be added to the input.conf file.
        '';
      };
      mpv = lib.mkOption {
        type = mpvini;
        example = {
          keep-open = "yes";
          osd-fractions = "yes";
        };
        description = ''
          A map of mpv options.
        '';
      };
    };
  };
  config = lib.mkIf cfg.enable {
    environment.systemPackages = [ wrappedMpv ];
  };
}