{- git-annex command
 -
 - Copyright 2011-2023 Joey Hess <id@joeyh.name>
 -
 - Licensed under the GNU AGPL version 3 or higher.
 -}

{-# LANGUAGE OverloadedStrings #-}

module Command.Reinject where

import Command
import Logs.Location
import Annex.Content
import Backend
import Types.KeySource
import Utility.Metered
import Annex.WorkTree
import qualified Git
import qualified Annex

cmd :: Command
cmd = withAnnexOptions [backendOption, jsonOptions] $
	command "reinject" SectionUtility 
		"inject content of file back into annex"
		(paramRepeating (paramPair "SRC" "DEST"))
		(seek <$$> optParser)

data ReinjectOptions = ReinjectOptions
	{ params :: CmdParams
	, knownOpt :: Bool
	, guessKeysOpt :: Bool
	}

optParser :: CmdParamsDesc -> Parser ReinjectOptions
optParser desc = ReinjectOptions
	<$> cmdParams desc
	<*> switch
		( long "known"
		<> help "inject all known files"
		<> hidden
		)
	<*> switch
		( long "guesskeys"
		<> help "inject files that are named like keys"
		<> hidden
		)

seek :: ReinjectOptions -> CommandSeek
seek os
	| guessKeysOpt os && knownOpt os = giveup "Cannot combine --known with --guesskeys"
	| guessKeysOpt os = withStrings (commandAction . startGuessKeys) (params os)
	| knownOpt os = withStrings (commandAction . startKnown) (params os)
	| otherwise = withPairs (commandAction . startSrcDest) (params os)

startSrcDest :: (SeekInput, (String, String)) -> CommandStart
startSrcDest (si, (src, dest))
	| src == dest = stop
	| otherwise = starting "reinject" ai si $ notAnnexed src' $
		lookupKey (toRawFilePath dest) >>= \case
			Just key -> ifM (verifyKeyContent key src')
				( perform src' key
				, do
					qp <- coreQuotePath <$> Annex.getGitConfig
					giveup $ decodeBS $ quote qp $ QuotedPath src'
						<> " does not have expected content of "
						<> QuotedPath (toRawFilePath dest)
				)
			Nothing -> do
				qp <- coreQuotePath <$> Annex.getGitConfig
				giveup $ decodeBS $ quote qp $ QuotedPath src'
					<> " is not an annexed file"
  where
	src' = toRawFilePath src
	ai = ActionItemOther (Just (QuotedPath src'))

startGuessKeys :: FilePath -> CommandStart
startGuessKeys src = starting "reinject" ai si $ notAnnexed src' $
	case fileKey (toRawFilePath (takeFileName src)) of
		Just key -> ifM (verifyKeyContent key src')
			( perform src' key
			, do
				qp <- coreQuotePath <$> Annex.getGitConfig
				giveup $ decodeBS $ quote qp $ QuotedPath src'
					<> " does not have expected content"
			)
		Nothing -> do
			warning "Not named like an object file; skipping"
			next $ return True
  where
	src' = toRawFilePath src
	ai = ActionItemOther (Just (QuotedPath src'))
	si = SeekInput [src]

startKnown :: FilePath -> CommandStart
startKnown src = starting "reinject" ai si $ notAnnexed src' $ do
	(key, _) <- genKey ks nullMeterUpdate =<< defaultBackend
	ifM (isKnownKey key)
		( perform src' key
		, do
			warning "Not known content; skipping"
			next $ return True
		)
  where
	src' = toRawFilePath src
	ks = KeySource src' src' Nothing
	ai = ActionItemOther (Just (QuotedPath src'))
	si = SeekInput [src]

notAnnexed :: RawFilePath -> CommandPerform -> CommandPerform
notAnnexed src a = 
	ifM (fromRepo Git.repoIsLocalBare)
		( a
		, lookupKey src >>= \case
			Just _ -> do
				qp <- coreQuotePath <$> Annex.getGitConfig
				giveup $ decodeBS $ quote qp $ 
					"cannot used annexed file as src: "
						<> QuotedPath src
			Nothing -> a
		)

perform :: RawFilePath -> Key -> CommandPerform
perform src key = do
	maybeAddJSONField "key" (serializeKey key)
	ifM move
		( next $ cleanup key
		, giveup "failed"
		)
  where
	move = checkDiskSpaceToGet key Nothing False $
		moveAnnex key (AssociatedFile Nothing) src

cleanup :: Key -> CommandCleanup
cleanup key = do
	logStatus key InfoPresent
	return True