- | Execute a git-vogue command.
runCommand
    :: forall m. (Applicative m, MonadIO m, Functor m)
    => VogueCommand
    -> SearchMode
    -> VCS m
    -> PluginDiscoverer m
    -> m ()
runCommand cmd search_mode VCS{..} PluginDiscoverer{..} = go cmd
  where
    cd = getTopLevel >>= liftIO . setCurrentDirectory
    go CmdInit = do
        cd
        already_there <- checkHook
        if already_there
            then success "Pre-commit hook is already installed"
            else do
                installHook
                installed <- checkHook
                if installed
                    then success "Successfully installed hook"
                    else failure "Hook failed to install"

    go CmdVerify = do
        cd
        installed <- checkHook
        if installed
            then success "Pre-commit hook currently installed"
            else failure "Pre-commit hook not installed"

    go CmdPlugins = do
        liftIO $  T.putStrLn "git-vogue knows about the following plugins:\n"
        discoverPlugins >>= liftIO . traverse_ print

    go (CmdDisable plugin) = do
        plugins <- discoverPlugins
        if plugin `elem` fmap pluginName (filter (not . enabled) plugins)
            then success "Plugin already disabled"
            else
                if plugin `elem` fmap pluginName plugins
                    then do
                        disablePlugin plugin
                        success "Disabled plugin"
                    else
                        failure "Unknown plugin"


    go (CmdEnable plugin) = do
        ps <- discoverPlugins
        if plugin `elem` fmap pluginName ps
            then
                if plugin `elem` (pluginName <$> filter (not . enabled) ps)
                    then do
                        enablePlugin plugin
                        success "Enabled plugin"
                    else
                        success "Plugin already enabled"
            else
                failure "Unknown plugin"

    go CmdRunCheck = do
        (check_fs, all_fs, plugins) <- things
        rs <- for plugins $ \p -> do
            r <- runCheck p check_fs all_fs
            liftIO . T.putStrLn $ colorize p r
            return r
        exitWithWorst rs


    go CmdRunFix = do
        (check_fs, all_fs, plugins) <- things
        rs <- for plugins $ \p -> do
            r <- runCheck p check_fs all_fs
            case r of
                Failure{} -> do
                    r' <- runFix p check_fs all_fs
                    liftIO . T.putStrLn $ colorize p r'
                    return $ Just r'
                _  -> return Nothing

        exitWithWorst (catMaybes rs)

    things = do
        cd
        check_fs <- getFiles search_mode
        when (null check_fs) (success "Vacuous success - Nothing to check")

        plugins <- filter enabled <$> discoverPlugins
        when (null check_fs) (success "Vacuous success - No plugins enabled")

        all_fs <- getFiles FindAll
        return (check_fs, all_fs, plugins)

runCommand
    :: forall m. (Applicative m, MonadIO m, Functor m)
    => VogueCommand
    -> SearchMode
    -> VCS m
    -> PluginDiscoverer m
    -> m ()
runCommand cmd search_mode VCS{..} PluginDiscoverer{..} = go cmd
  where
    cd = getTopLevel >>= liftIO . setCurrentDirectory
    go CmdInit = do
        cd
        already_there <- checkHook
        if already_there
            then success "Pre-commit hook is already installed"
            else do
                installHook
                installed <- checkHook
                if installed
                    then success "Successfully installed hook"
                    else failure "Hook failed to install"

    go CmdVerify = do
        cd
        installed <- checkHook
        if installed
            then success "Pre-commit hook currently installed"
            else failure "Pre-commit hook not installed"

    go CmdPlugins = do
        liftIO $  T.putStrLn "git-vogue knows about the following plugins:\n"
        discoverPlugins >>= liftIO . traverse_ print

    go (CmdDisable plugin) = do
        plugins <- discoverPlugins
        if plugin `elem` fmap pluginName (filter (not . enabled) plugins)
            then success "Plugin already disabled"
            else
                if plugin `elem` fmap pluginName plugins
                    then do
                        disablePlugin plugin
                        success "Disabled plugin"
                    else
                        failure "Unknown plugin"


    go (CmdEnable plugin) = do
        ps <- discoverPlugins
        if plugin `elem` fmap pluginName ps
            then
                if plugin `elem` (pluginName <$> filter (not . enabled) ps)
                    then do
                        enablePlugin plugin
                        success "Enabled plugin"
                    else
                        success "Plugin already enabled"
            else
                failure "Unknown plugin"

    go CmdRunCheck = do
        (check_fs, all_fs, plugins) <- things
        rs <- for plugins $ \p -> do
            r <- runCheck p check_fs all_fs
            liftIO . T.putStrLn $ colorize p r
            return r
        exitWithWorst rs


    go CmdRunFix = do
        (check_fs, all_fs, plugins) <- things
        rs <- for plugins $ \p -> do
            r <- runCheck p check_fs all_fs
            case r of
                Failure{} -> do
                    r' <- runFix p check_fs all_fs
                    liftIO . T.putStrLn $ colorize p r'
                    return $ Just r'
                _  -> return Nothing

        exitWithWorst (catMaybes rs)

    things = do
        cd
        check_fs <- getFiles search_mode
        when (null check_fs) (success "Vacuous success - Nothing to check")

        plugins <- filter enabled <$> discoverPlugins
        when (null check_fs) (success "Vacuous success - No plugins enabled")

        all_fs <- getFiles FindAll
        return (check_fs, all_fs, plugins)
runCommand cmd search_mode VCS{..} PluginDiscoverer{..} = go cmd
  where
    cd = getTopLevel >>= liftIO . setCurrentDirectory
    go CmdInit = do
        cd
        already_there <- checkHook
        if already_there
            then success "Pre-commit hook is already installed"
            else do
                installHook
                installed <- checkHook
                if installed
                    then success "Successfully installed hook"
                    else failure "Hook failed to install"

    go CmdVerify = do
        cd
        installed <- checkHook
        if installed
            then success "Pre-commit hook currently installed"
            else failure "Pre-commit hook not installed"

    go CmdPlugins = do
        liftIO $  T.putStrLn "git-vogue knows about the following plugins:\n"
        discoverPlugins >>= liftIO . traverse_ print

    go (CmdDisable plugin) = do
        plugins <- discoverPlugins
        if plugin `elem` fmap pluginName (filter (not . enabled) plugins)
            then success "Plugin already disabled"
            else
                if plugin `elem` fmap pluginName plugins
                    then do
                        disablePlugin plugin
                        success "Disabled plugin"
                    else
                        failure "Unknown plugin"


    go (CmdEnable plugin) = do
        ps <- discoverPlugins
        if plugin `elem` fmap pluginName ps
            then
                if plugin `elem` (pluginName <$> filter (not . enabled) ps)
                    then do
                        enablePlugin plugin
                        success "Enabled plugin"
                    else
                        success "Plugin already enabled"
            else
                failure "Unknown plugin"

    go CmdRunCheck = do
        (check_fs, all_fs, plugins) <- things
        rs <- for plugins $ \p -> do
            r <- runCheck p check_fs all_fs
            liftIO . T.putStrLn $ colorize p r
            return r
        exitWithWorst rs


    go CmdRunFix = do
        (check_fs, all_fs, plugins) <- things
        rs <- for plugins $ \p -> do
            r <- runCheck p check_fs all_fs
            case r of
                Failure{} -> do
                    r' <- runFix p check_fs all_fs
                    liftIO . T.putStrLn $ colorize p r'
                    return $ Just r'
                _  -> return Nothing

        exitWithWorst (catMaybes rs)

    things = do
        cd
        check_fs <- getFiles search_mode
        when (null check_fs) (success "Vacuous success - Nothing to check")

        plugins <- filter enabled <$> discoverPlugins
        when (null check_fs) (success "Vacuous success - No plugins enabled")

        all_fs <- getFiles FindAll
        return (check_fs, all_fs, plugins)