local DEFAULT_DISK_LABEL = 'TrapOS Disk';
local DEFAULT_INSTALL_URL = 'https://os.trapcloud.fr/install';
local DEFAULT_SETTINGS_SOURCE_PATH = '/.settings';

local function luaQuote(value)
  return string.format('%q', tostring(value));
end

local function isValidMode(mode)
  return mode == 'install' or mode == 'reinstall' or mode == 'uninstall';
end

local function writeFile(path, body)
  local file = fs.open(path, 'w');
  if not file then return false; end
  file.write(body);
  file.close();
  return true;
end

local function createInstallerDisk(opts)
  opts = opts or {};
  local peripheralApi = opts.peripheral or peripheral;
  local settingsApi = opts.settings or settings;
  local settingsSourcePath = opts.settingsSourcePath or DEFAULT_SETTINGS_SOURCE_PATH;
  local diskLabel = opts.diskLabel or opts.installerLabel or DEFAULT_DISK_LABEL;
  local installUrl = opts.installUrl or DEFAULT_INSTALL_URL;

  local api = {};

  local function buildHeader(mode)
    return '-- TrapOS ' .. mode .. ' disk autorun (generated; do not edit)\n'
      .. 'local DISK_LABEL = ' .. luaQuote(diskLabel) .. ';\n'
      .. 'local INSTALL_URL = ' .. luaQuote(installUrl) .. ';\n'
      .. '\n';
  end

  local function buildDiskMountFunction()
    return 'local function diskMount()\n'
      .. '  for _, name in ipairs(peripheral.getNames()) do\n'
      .. "    if peripheral.getType(name) == 'drive' then\n"
      .. '      local drive = peripheral.wrap(name);\n'
      .. '      if drive.isDiskPresent() and drive.getDiskLabel() == DISK_LABEL then\n'
      .. '        return drive.getMountPath();\n'
      .. '      end\n'
      .. '    end\n'
      .. '  end\n'
      .. 'end\n'
      .. '\n';
  end

  local function buildWriteMarkerFunction()
    return 'local function writeMarker(path)\n'
      .. "  local file = fs.open(path, 'w');\n"
      .. '  if not file then return false; end\n'
      .. "  file.write('1');\n"
      .. '  file.close();\n'
      .. '  return true;\n'
      .. 'end\n'
      .. '\n';
  end

  function api.buildAutorun(mode)
    mode = mode or 'install';
    if not isValidMode(mode) then
      error('invalid disk mode: ' .. tostring(mode), 2);
    end

    if mode == 'install' then
      return buildHeader(mode)
        .. "if fs.exists('/trapos') then return; end\n"
        .. '\n'
        .. buildDiskMountFunction()
        .. 'local mount = diskMount();\n'
        .. 'if mount then\n'
        .. "  local src = fs.combine(mount, '.settings');\n"
        .. '  if fs.exists(src) then\n'
        .. "    fs.delete('/.settings');\n"
        .. "    fs.copy(src, '/.settings');\n"
        .. '    settings.load();\n'
        .. '  end\n'
        .. 'end\n'
        .. '\n'
        .. "print('TrapOS Disk: installing TrapOS...');\n"
        .. "if not shell.run('wget', 'run', INSTALL_URL) then\n"
        .. "  print('TrapOS install failed.');\n"
        .. 'end\n';
    end

    if mode == 'uninstall' then
      return buildHeader(mode)
        .. "if not fs.exists('/trapos') then\n"
        .. "  print('TrapOS already removed.');\n"
        .. 'else\n'
        .. "  print('TrapOS Disk: uninstalling TrapOS...');\n"
        .. "  if not shell.run('ccpm', 'uninstall', 'trapos', '--yes') then\n"
        .. "    print('TrapOS uninstall failed.');\n"
        .. '    return;\n'
        .. '  end\n'
        .. 'end\n'
        .. '\n'
        .. "print('TrapOS removed. Eject the disk to finish.');\n"
        .. "os.pullEvent('disk_eject');\n"
        .. 'os.reboot();\n';
    end

    return buildHeader(mode)
      .. buildDiskMountFunction()
      .. buildWriteMarkerFunction()
      .. 'local mount = diskMount();\n'
      .. 'if not mount then\n'
      .. "  print('TrapOS Disk not found.');\n"
      .. '  return;\n'
      .. 'end\n'
      .. "local MARKER = fs.combine(mount, '.trapos-reinstalled');\n"
      .. "if fs.exists(MARKER) and fs.exists('/trapos') then\n"
      .. "  print('Reinstall complete. Eject the disk.');\n"
      .. "  os.pullEvent('disk_eject');\n"
      .. '  os.reboot();\n'
      .. '  return;\n'
      .. 'elseif fs.exists(MARKER) then\n'
      .. '  fs.delete(MARKER);\n'
      .. 'end\n'
      .. "if fs.exists('/trapos') then\n"
      .. "  print('TrapOS Disk: removing current TrapOS...');\n"
      .. "  if not shell.run('ccpm', 'uninstall', 'trapos', '--yes') then\n"
      .. "    print('TrapOS uninstall failed.');\n"
      .. '    return;\n'
      .. '  end\n'
      .. 'end\n'
      .. 'if not writeMarker(MARKER) then\n'
      .. "  print('Failed to write reinstall marker.');\n"
      .. '  return;\n'
      .. 'end\n'
      .. "print('TrapOS Disk: reinstalling TrapOS...');\n"
      .. "if not shell.run('wget', 'run', INSTALL_URL) then\n"
      .. '  fs.delete(MARKER);\n'
      .. "  print('TrapOS install failed.');\n"
      .. '  return;\n'
      .. 'end\n';
  end

  function api.findTargetDrive()
    local names = peripheralApi.getNames and peripheralApi.getNames() or {};
    local presentCount = 0;
    local nonEmptyCount = 0;
    local empty = {};

    for _, name in ipairs(names) do
      if peripheralApi.getType(name) == 'drive' then
        local drive = peripheralApi.wrap(name);
        if drive.isDiskPresent() and drive.hasData() then
          presentCount = presentCount + 1;
          local mountPath = drive.getMountPath();
          if mountPath and #fs.list(mountPath) == 0 then
            empty[#empty + 1] = { name = name, mountPath = mountPath, drive = drive };
          else
            nonEmptyCount = nonEmptyCount + 1;
          end
        end
      end
    end

    if #empty > 1 then
      return false, 'multiple blank disks found, remove all but one';
    end
    if #empty == 1 then
      return true, empty[1];
    end
    if presentCount > 0 or nonEmptyCount > 0 then
      return false, 'disk not empty; insert a blank floppy';
    end
    return false, 'insert a blank floppy';
  end

  function api.create(createOpts)
    createOpts = createOpts or {};
    local mode = createOpts.mode or 'install';
    if not isValidMode(mode) then
      return false, 'invalid disk mode: ' .. tostring(mode);
    end

    local ok, target = api.findTargetDrive();
    if not ok then return false, target; end

    target.drive.setDiskLabel(diskLabel);

    local result = {
      name = target.name,
      mountPath = target.mountPath,
      label = diskLabel,
      mode = mode,
      settingsCopied = false,
      settingsWarning = false,
    };

    if mode == 'install' and fs.exists(settingsSourcePath) then
      settingsApi.save();
      local dest = fs.combine(target.mountPath, '.settings');
      fs.delete(dest);
      fs.copy(settingsSourcePath, dest);
      result.settingsCopied = true;
    elseif mode == 'install' then
      result.settingsWarning = true;
    end

    local startupPath = fs.combine(target.mountPath, 'startup.lua');
    fs.delete(startupPath);
    if not writeFile(startupPath, api.buildAutorun(mode)) then
      return false, 'failed to write ' .. startupPath;
    end

    return true, result;
  end

  return api;
end

return createInstallerDisk;
