local createCcpm = require('/apis/libccpm');
local createVersion = require('/apis/libversion');

local args = table.pack(...);
local command = args[1];

local function printUsage()
  print('ccpm usage:');
  print();
  print('\t\tccpm install <package>');
  print('\t\tccpm reinstall <package>');
  print('\t\tccpm uninstall <package> [--yes] [--no-prune]');
  print('\t\tccpm prune');
  print('\t\tccpm update');
  print('\t\tccpm upgrade');
  print('\t\tccpm ls');
  print('\t\tccpm available [term]');
  print('\t\tccpm search [term]');
  print('\t\tccpm info <package>');
  print('\t\tccpm registry ls');
  print('\t\tccpm registry add <name> [--branch <b>] [--type gitea|http]');
  print('\t\tccpm registry rm <name>');
  print('\t\tccpm version');
  print('\t\tccpm help');
end

if command == 'version' or command == '-version' or command == '--version' then
  print('v' .. createVersion().forSelf());
  return;
end

if command == nil or command == '' or command == 'help' or command == '-help' or command == '--help' then
  printUsage();
  return;
end

local ccpm = createCcpm();

local function logLine(msg)
  print(msg);
end

local function printColored(msg, color)
  if term.isColor and term.isColor() then
    local previous = term.getTextColor();
    term.setTextColor(color);
    print(msg);
    term.setTextColor(previous);
  else
    print(msg);
  end
end

local function readLine()
  if type(read) == 'function' then return read(); end
  if io and type(io.read) == 'function' then return io.read(); end
  return '';
end

local function confirm(prompt)
  print(prompt .. ' [y/N]');
  local answer = string.lower(readLine() or '');
  return answer == 'y' or answer == 'yes';
end

local function parseUninstallArgs(rawArgs)
  local pkg = nil;
  local yes = false;
  local noPrune = false;
  local parseError = nil;

  local i = 2;
  while i <= rawArgs.n do
    local arg = rawArgs[i];
    if arg == '--yes' or arg == '-y' then
      yes = true;
    elseif arg == '--no-prune' then
      noPrune = true;
    elseif arg and string.sub(arg, 1, 1) == '-' then
      parseError = 'unknown option: ' .. arg;
    elseif not pkg then
      pkg = arg;
    else
      parseError = 'unexpected argument: ' .. arg;
    end
    i = i + 1;
  end

  return pkg, { yes = yes, prune = not noPrune }, parseError;
end

local function printAvailableRow(r)
  local installed = '';
  if r.installedVersion then
    installed = ' installed v' .. tostring(r.installedVersion);
  end
  local line = r.name .. ' v' .. tostring(r.version) .. ' (' .. r.registry .. ') ' .. r.status .. installed;
  if r.status == 'up-to-date' then
    printColored(line, colors.lime);
  elseif r.status == 'updatable' then
    printColored(line, colors.orange);
  else
    print(line);
  end
end

if command == 'install' or command == 'reinstall' then
  local pkg = args[2];
  if not pkg then printUsage(); return; end
  local ok, result = ccpm.install(pkg, { force = command == 'reinstall', log = logLine });
  if not ok then
    print(result);
    return;
  end
  print('=> ' .. pkg .. ' installed.');
  return;
end

if command == 'update' then
  local cache = ccpm.update();
  local count = 0;
  for _ in pairs(cache.packages or {}) do count = count + 1; end
  print('=> package cache updated (' .. count .. ' packages).');
  return;
end

if command == 'upgrade' then
  local ok, result = ccpm.upgrade({ log = logLine });
  if not ok then
    print(result);
    return;
  end
  if #result == 0 then
    print('=> all packages up-to-date.');
  else
    print('=> upgraded: ' .. table.concat(result, ', '));
  end
  return;
end

if command == 'prune' then
  local removed = ccpm.prune({ log = logLine });
  if #removed == 0 then
    print('=> nothing to prune.');
  else
    print('=> pruned: ' .. table.concat(removed, ', '));
  end
  return;
end

if command == 'uninstall' or command == 'remove' or command == 'rm' then
  local pkg, uninstallOpts, parseError = parseUninstallArgs(args);
  if parseError then
    print(parseError);
    printUsage();
    return;
  end
  if not pkg then printUsage(); return; end

  if not uninstallOpts.yes then
    local planOk, plan = ccpm.planUninstall(pkg, { prune = uninstallOpts.prune });
    if not planOk then
      print(plan);
      return;
    end
    print('The following packages will be removed:');
    for _, name in ipairs(plan) do
      print('  ' .. name);
    end
    if not confirm('Continue?') then
      print('=> uninstall cancelled.');
      return;
    end
  end

  local ok, result = ccpm.uninstall(pkg, { prune = uninstallOpts.prune, log = logLine });
  if not ok then
    print(result);
    return;
  end
  if type(result) == 'table' and #result > 1 then
    print('=> removed: ' .. table.concat(result, ', '));
  else
    print('=> ' .. pkg .. ' uninstalled.');
  end
  return;
end

if command == 'ls' or command == 'list' then
  local packages = ccpm.list();
  local names = {};
  for name in pairs(packages) do names[#names + 1] = name; end
  table.sort(names);
  if #names == 0 then
    print('No packages installed.');
    return;
  end
  for _, name in ipairs(names) do
    print(name .. ' v' .. tostring(packages[name].version or '?'));
  end
  return;
end

if command == 'search' then
  local results = ccpm.search(args[2]);
  if #results == 0 then
    print('No packages found.');
    return;
  end
  for _, r in ipairs(results) do
    print(r.name .. ' v' .. tostring(r.version) .. ' (' .. r.registry .. ')');
  end
  return;
end

if command == 'available' then
  local results = ccpm.available(args[2]);
  if #results == 0 then
    print("No packages found. Run 'ccpm update' first if the cache is empty.");
    return;
  end
  for _, r in ipairs(results) do
    printAvailableRow(r);
  end
  return;
end

if command == 'info' then
  local pkg = args[2];
  if not pkg then printUsage(); return; end
  local desc = ccpm.info(pkg);
  if not desc then
    print('package not found: ' .. pkg);
    return;
  end
  print(desc.name .. ' v' .. tostring(desc.version or '?'));
  if desc.description then print(desc.description); end
  if desc.dependencies and #desc.dependencies > 0 then
    print('dependencies: ' .. table.concat(desc.dependencies, ', '));
  end
  print('files:');
  for _, f in ipairs(desc.files or {}) do print('  ' .. f); end
  return;
end

if command == 'registry' then
  local sub = args[2];

  if sub == nil or sub == 'ls' or sub == 'list' then
    local registries = ccpm.listRegistries();
    if #registries == 0 then
      print('No registries configured.');
      return;
    end
    for _, r in ipairs(registries) do
      if r.type == 'gitea' then
        print(r.name .. ' (gitea:' .. tostring(r.branch or 'master') .. ')');
      else
        print(r.name .. ' (' .. tostring(r.type or 'http') .. ')');
      end
    end
    return;
  end

  if sub == 'add' then
    local name = args[3];
    if not name then printUsage(); return; end
    local branch, rtype;
    local i = 4;
    while i <= args.n do
      local a = args[i];
      if a == '--branch' then
        branch = args[i + 1];
        i = i + 1;
      elseif a == '--type' then
        rtype = args[i + 1];
        i = i + 1;
      end
      i = i + 1;
    end
    local ok, err = ccpm.addRegistry(name, { branch = branch, type = rtype });
    if not ok then
      print(err);
      return;
    end
    print('=> registry added: ' .. name);
    return;
  end

  if sub == 'rm' or sub == 'remove' then
    local name = args[3];
    if not name then printUsage(); return; end
    local ok, err = ccpm.removeRegistry(name);
    if not ok then
      print(err);
      return;
    end
    print('=> registry removed: ' .. name);
    return;
  end

  printUsage();
  return;
end

printUsage();
